Arc Forumnew | comments | leaders | submitlogin
"as" macro for a more readable "coerce"
9 points by aw 5517 days ago | 9 comments
I seem to write a lot of code which looks like:

  (coerce (blah blah blah blah) 'int)
The more stuff I have in the middle the harder it is for me to keep track of what I'm coercing the expression into.

For example, here I'm reading a number from the input, then reading that number of bytes, and converting the bytes to a string:

  (def read-bytes ()
    (let n (coerce (readline) 'int)
      (coerce (map [coerce _ 'char] (n-of n (readb))) 'string)))
Here's an "as" macro, which swaps the arguments to "coerce", and quotes the type:

  (mac as (type x)
    `(coerce ,x ',type))
I find this version easier to understand:

  (def read-bytes ()
    (let n (as int (readline))
      (as string (map [as char _] (n-of n (readb))))))


2 points by rocketnia 5517 days ago | link

I was thinking the same thing! I wonder if there's a more widely-applicable convention this can tend toward: Put simple, enum-like arguments at the beginning, so that they can blend in with the function name.

You could do the same thing with isa. I'm not sure what it would best be called, but it would make acons even more redundant.

  (mac a- (type x)
    `(isa ,x ',type))
  (def another-acons (x)
    (a- cons x))
This particular example probably isn't quite as useful; if I have a complex expression, I usually want to use the result itself at some point, rather than just passing it to isa. Maybe there's a better example out there, though.

-----

1 point by akkartik 5516 days ago | link

> Put simple, enum-like arguments at the beginning, so that they can blend in with the function name.

Languages like haskell do a better job explicitly encouraging this practice - you can curry functions with frequently-used args. Eventually you figure out that it's useful to put curryable arguments at the start.

-----

2 points by aw 5515 days ago | link

Though with Arc's "string" and "int" already available, my function could be written as:

  (def read-bytes ()
    (let n (int (readline))
      (string (map [coerce _ 'char] (n-of n (readb))))))
and with an analogous "char":

  (def char (x)
    (coerce x 'char))
my function is even shorter:

  (def read-bytes ()
    (let n (int (readline))
      (string (map char (n-of n (readb))))))
so perhaps I don't need "as" if I learn to use Arc's derived conversion functions :-)

-----

2 points by thaddeus 5516 days ago | link

'as' is much better - changing my code now :)

I had also created coerce-if for coercing args from an defop arg-req:

  (def coerce-if (v type) 
     "coerce value if not nil"
     (if v (coerce v type)))
only when substituting for "as" I start getting arc-slang-talk:

  (def as-if (type v) 
     (if v (coerce v type)))

   (defop myserv req
      (let id (as-if int (arg-req "id"))
        (pr id))
lol.

-----

1 point by akkartik 5516 days ago | link

More than as-if, I want a really-as, which will convert nil to a default value.

Right now I just have int2, which is like int, but converts nil to 0. Still mulling how to generalize it. Perhaps it doesn't need to be generalized.

Anybody have a better name than int2?

-----

2 points by aw 5516 days ago | link

Maybe a function like 'only', but which defaults to 0 instead of nil?

  (def default0 (f)
    (fn args (if (car args) (apply f args) 0)))

  arc> (default0.int "123")
  123
  arc> (default0.int nil)
  0

-----

1 point by rocketnia 5515 days ago | link

You could even name it "0nly", with a zero. ^_^ That could be a bit deceptive, though.

-----

2 points by aw 5516 days ago | link

Here's a version that can call "coerce" with additional arguments when needed:

  (mac as (type x . args)
    `(coerce ,x ',type ,@args))

  arc> (as string 256 16)
  "100"

-----

1 point by palsecam 5516 days ago | link

Very good idea. Thanks. It's in the spirit of 'in, which I like a lot, too.

-----