Arc Forumnew | comments | leaders | submitlogin
2 points by shader 3874 days ago | link | parent

My original goal was not to shave characters off of '#:key. I apparently misunderstood how they were supposed to work, and thought that arc wasn't compatible with them. I ended up doing the cosmetic change as part of my process of figuring out how to hack it onto arc.

If what you're saying about '#:key vs #:key is true though, then my test cases were incorrect and I was trying to make something work that shouldn't have.

So, what's the right way to hack on arc to support calling racket functions with keyword args? Or would it be better to make a more arc-idiomatic mongo driver, and how?



2 points by rocketnia 3873 days ago | link

Oops, I think you've been seeing something I wasn't seeing.

It turns out I'm getting much different results on a local setup than I was getting in tryarc.org. Since Arc 3.1 and Anarki use (require mzscheme), all sorts of things are replaced with doppelgangers that doesn't support keyword args, including the function application syntax #%app. In fact the lack of keyword arguments is one of the only things (require mzscheme) is good for. This is something tryarc.org changes, apparently.

MzScheme docs: http://docs.racket-lang.org/mzscheme/index.html

---

"So, what's the right way to hack on arc to support calling racket functions with keyword args?"

I wouldn't say it's "the right way," but we could put the functionality of Racket's 'keyword-apply in a function that takes an Arc table, and then we'd pretty much never need to worry about keywords other than that.

I have an implementation for this, which I should be able to commit with some unit tests now that I know what's going on with MzScheme.

...Actually it might take me a few days to get around to that, so here's the code if anyone wants to use it right away:

  (def $kw-apply (func kwargs . posargs)
    
    ; Convert the last element of posargs (the first-class list) into a
    ; Racket list. If posargs is completely empty, pretend the last
    ; element is an empty list just like Arc's 'apply already does.
    (zap [rev:aif rev._
           (cons (apply $.list car.it) cdr.it)
           (list:$.list)]
         posargs)
    
    (let (ks vs)
         (apply map list  ; Transpose.
           (sort (compare $.keyword<? !0)
             (map [list ($.string->keyword:+ "" (or _.0 "nil")) _.1.0]
               tablist.kwargs)))
      (apply $.keyword-apply func (apply $.list ks) (apply $.list vs)
        posargs)))
Use it like so:

  ($kw-apply my-func
    
    ; keyword args
    (obj a list.1 b list.2 message (list "hello"))
    
    ; positional args
    1 2 3 (list 4 5))
I'm using a table of singleton lists so that we can pass the symbol 'nil as an argument value.

---

"Or would it be better to make a more arc-idiomatic mongo driver, and how?"

I actually have some opinion about "the best way" for this. :)

I think Arc-idiomatic approaches serve no particular purpose, since Arc is a tool for general-purpose computation.[1] I would want a database driver to be idiomatic only for the database itself, so that it serves the more specific purpose of storing data. This can then be accompanied with sugary helper utilities, as long as they're optional.

It seems many ORMs want to bake the sugar into the interface, or they make sugar that has tiny escape hatches for poking at the underlying interface. If sugar is the only thing a programmer (usually) sees, I look at it as though it's a full-on database design of its own... which is rarely favorable since it usually inherits most of the complexity and obligations of the original.

[1] Well, while Arc is a general-purpose tool, it's specifically a language, so Arc-idiomatic approaches serve the particular purpose of making features more accessible to language users.

-----

3 points by shader 3873 days ago | link

Do we actually need the mzscheme dependency? Any reason we couldn't switch to a full racket base?

---

Interesting. I'll have to play around with that. And study it a bit more to figure out how it works. If I understand your description correctly, the reason you're using lists is so that 'nil is interpreted as no value, while '(nil) is interpreted as intentionally passing the value 'nil? How does the function know the difference?

---

Interesting opinion. Unless I misunderstood you, I would have felt the opposite way. Normally, I would expect the purpose of the data interaction layer to be separating the implementation details from the code, so that if changes need to be made to what backend storage system you can just trade it out.

Maybe that is what you mean though, and that's supposed to be a distinction between the "driver" and an additional abstraction layer. Though your comment about ORMs including sugar confuses that a bit. I don't really want sugar per se, just abstraction away from how I'm actually storing the data, within reason anyway.

-----

2 points by rocketnia 3873 days ago | link

"Do we actually need the mzscheme dependency? Any reason we couldn't switch to a full racket base?"

There's always a reason, but these days Anarki has broke all my code enough times that that shouldn't be a concern. :)

I think this would be a positive change.

---

"...the reason you're using lists is so that 'nil is interpreted as no value, while '(nil) is interpreted as intentionally passing the value 'nil? How does the function know the difference?"

Arc doesn't support nil as an element of a table. Setting a table entry to nil removes it. Therefore '$kw-apply will only see non-nil values anyway.

As pg says: "In situations where the values you're storing might be nil, you just enclose all the values in lists." http://arclanguage.org/item?id=493

When I was using Arc heavily, I defined a utility (sobj ...) that was just like (obj ...) but it wrapped everything in a singleton list. That may have been the only extra utility I needed.

I could write (each (k (v)) tab ...) in place of (each (k v) tab ...). I could write tab!key.0 in place of tab!key. I could write (iflet (v) tab!key ...) in place of (iflet v tab!key ...).

It was surprisingly unintrusive, even pleasant.

---

"Normally, I would expect the purpose of the data interaction layer to be separating the implementation details from the code, so that if changes need to be made to what backend storage system you can just trade it out."

I like the sound of that, but I think it's always a bit leaky, unless the result is a full database design that lets people happily forget there's another database underneath.

-----