Erlang 17 and Maps...

166 views
Skip to first unread message

dun...@cogitat.io

unread,
Feb 4, 2014, 10:20:20 PM2/4/14
to lisp-flavo...@googlegroups.com
Robert,

Have you given any thought to how you might want to handle the syntax for maps in LFE?

I've always felt that in Common Lisp hash-table, alist, and plist were too awkward for creating data structures. Conversely, I admit having fallen in love with the Clojure syntax for hash maps ;-)

I find myself doing things like this in LFE:

(: dict from_list `(#(a 1) #(b 2)))

or with a rename:

(tuples->dict `(#(a 1) #(b 2)))

I would love to be able to use maps like this:

#{a 1 b 2}

or

(hash-map 'a 1 'b 2)

Obviously, the first strays pretty far from anything like CL syntax ... :-/  But it's a nice match with the Erlang syntax. Maybe something similar, using just parens and a special char?

%(a 1 b 2)
=>(a 1 b 2)

Dunno. That last one's ugly ! I guess if we were going to use 2 chars, it might as well be this instead:

##(a 1 b 2)

Anyway, what are your thoughts?

d

Sean Chalmers

unread,
Feb 5, 2014, 5:48:25 AM2/5/14
to lisp-flavo...@googlegroups.com
Heya,

I have to admit I enjoy the syntax from Clojure for hash maps as well, but when I'm writing Erlang, even LFE, I still associate {} with tuples, and I think it would be a shame to not take advantage of the '=>' and ':=' tokens from the new map syntax. So maybe something like

(set my-map (=> `foo 3.13 `bar `"steve")) ;; to create it

and

(:= my-map 'foo NewNum 'bar NewName) ;; to update/pattern match  ?

Of course how to add a key, I guess there could be (=> my-map 'boo 'bollocks) so the functions '=>' and ':=' have two forms, one where the first argument is a map which is considered an update attempt.. then we could chain them with a macro like (:=> 'foo 42 'boo 'bugger) to update and add?

I'm just spinning my wheels now, I'll stop.. :P

rvirding

unread,
Feb 9, 2014, 6:11:34 PM2/9/14
to lisp-flavo...@googlegroups.com
Sorry to disappoint you guys but my first idea was to use record like macro names map-new, map-get map-set, ... etc. Not very glamorous I know. :-)

(map-set my-map 'bert 53)

Robert

Duncan McGreggor

unread,
Feb 9, 2014, 8:57:03 PM2/9/14
to lisp-flavo...@googlegroups.com
I guess we could always write macros for the syntax we want ;-)

/me takes a deep, deep breath of that Lispy freedom ...

d


--
You received this message because you are subscribed to the Google Groups "Lisp Flavoured Erlang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lisp-flavoured-e...@googlegroups.com.
To post to this group, send email to lisp-flavo...@googlegroups.com.
Visit this group at http://groups.google.com/group/lisp-flavoured-erlang.
For more options, visit https://groups.google.com/groups/opt_out.

Duncan McGreggor

unread,
Mar 22, 2014, 3:59:13 PM3/22/14
to lisp-flavo...@googlegroups.com
Returning to this thread, now that I've posted some gist examples to the other thread. For quick reference, here are the gists:
  https://gist.github.com/oubiwann/9701054

Some quick thoughts from my tiny adventure:
 * Erlang syntax really is quite powerful; I continue to be impressed.
 * Clojure was by far the most enjoyable to work with.
 * Racket had the fullest and most useful set of hash functions (and best docs).
 * Chicken Scheme was probably second.
 * Common Lisp was probably (I hate to say it) the most awkward of the bunch). I'm hoping we can avoid pretty much everything the way it was done there :-/

One of the things that makes Clojure such a joy to work with is the unified aspect of core functions and how one uses these to manipulate data structures of different types. Most other implementations have functions/macros that are dedicated to working with just maps. While that's clean and definitely has a strong appeal, Clojure reflects a great deal of elegance.

That being said, I don't think today is the day to propose unifying features for LFE/Erlang data types ;-) (To be honest, though, it's *certainly* in the back of my mind... this is probably true for many others on the mail list.)

Given my positive experience with maps (hash tables) in Racket, and Robert's initial proposed functions like map-new, map-set, I'd encourage us to look to Racket for some inspiration:
  http://docs.racket-lang.org/reference/hashtables.html

Additional thoughts/concerns:

 * "map" has a specific meaning in FPs (: lists map), and there's a little bit of cognitive dissonance for me when I look at "map-*"

 * In my experience, applications generally don't have too many records; however, I've known apps with 100s and 1000s of instances of hash maps; as such, the idea of creating macros for each hash-map (e.g., my-map-get, my-map-set, ...) terrifies me a little. I don't believe this has been proposed, and I don't know enough about LFE's internals (much less, Erlang's) to be able to discuss this with any certainty.

 * The thought did occur that we could put all the map functions in a module e.g., (: maps new ... ), etc. Though, given that the functionality is built-in, I'm not too sure about this. I guess if we want to provide some of the functionality offered by Clojure and Racket, a maps module might be just the thing.

 * Still pondering the question of additional syntax ...

For reference, I'll paste some of the better APIs below:

Clojure:

Create a new map:
 * hash-map
 * sorted-map
 * sorted-map-by
Change a map:
 * assoc
 * dissoc
 * select-keys
 * merge
 * merge-with
 * zipmap
Examine a map:
 * get
 * contains?
 * find
 * keys
 * vals
 * map?
Examine a map entry:
 * key
 * val
Others:
 * count
 * conj
 * seq
 * rseq

Racket:

hash?
hash-equal?
hash-eqv?
hash-eq?
hash-weak?
hash
hasheq
hasheqv
make-hash
make-hasheqv
make-hasheq
make-weak-hash
make-weak-hasheqv
make-weak-hasheq
make-immutable-hash
make-immutable-hasheqv
make-immutable-hasheq
hash-set!
hash-set*!
hash-set
hash-set*
hash-ref
hash-ref!
hash-has-key?
hash-update!
hash-update
hash-remove!
hash-remove
hash-clear!
hash-clear
hash-copy-clear
hash-map
hash-keys
hash-values
hash->list
hash-for-each
hash-count
hash-empty?
hash-iterate-first
hash-iterate-next
hash-iterate-key
hash-iterate-value
hash-copy
eq-hash-code
eqv-hash-code
equal-hash-code
equal-secondary-hash-code

Chicken Scheme:

make-hash-table
hash-table?
hash-table-size
hash-table-equivalence-function
hash-table-hash-function
hash-table-min-load
hash-table-max-load
hash-table-weak-keys
hash-table-weak-values
hash-table-has-initial?
hash-table-initial
hash-table-keys
hash-table-values
hash-table->alist
alist->hash-table
hash-table-ref
hash-table-ref/default
hash-table-exists?
hash-table-set!
hash-table-update!
hash-table-update!/default
hash-table-copy
hash-table-delete!
hash-table-remove!
hash-table-clear!
hash-table-merge
hash-table-merge!
hash-table-map
hash-table-fold
hash-table-for-each
hash-table-walk


That's all for now :-)

d






On Sun, Feb 9, 2014 at 5:11 PM, rvirding <rvir...@gmail.com> wrote:

--

Duncan McGreggor

unread,
Mar 22, 2014, 6:09:53 PM3/22/14
to lisp-flavo...@googlegroups.com

On Sat, Mar 22, 2014 at 2:59 PM, Duncan McGreggor <dun...@cogitat.io> wrote:
>
> Returning to this thread, now that I've posted some gist examples to the other thread. For quick reference, here are the gists:
>   https://gist.github.com/oubiwann/9701054

[snip]

 
>
>  * Still pondering the question of additional syntax ...

Looking at this Erlang syntax:

A = #{key1 => Val1, key2 => Val2, ...}

My fingers want to do something like this:

(set a #((=> key1 "value 1") (=> key2 "value 2")))

That feels pretty natural, from the LFE perspective. However, it looks like it might require hacking on the tuple-parsing logic (or splitting that into two code paths: one for regular tuple-parsing, and the other for maps...).

The above syntax also lends itself nicely to these:

(set a #((:= key1 "value 1") (:= key2 "value 2")))
(set a #((=> key1 "value 1") (:= key2 "value 2")))


The question that arises for me is "how would we do this when calling functions?" Perhaps:

(set a (map (=> 'key1 '"value 1")))
(set a (map (:= 'key1 '"value 1")))


Or:

(set a (map (add-pair 'key1 '"value 1")))
(set a (map (update-pair 'key1 '"value 1")))

Then, for Joe's other example:

Z = #{{age, fred} => 12,
      {age, bill} => 97,
      {color, red} => {rgb, 255, 0, 0}}.

We'd have this for LFE:

(set a #((=> #(age fred) 12)
         (=> #(age bill) 97)
         (=> #(color red) #(rgb 200 0 0))))

Before we pattern match on this, let's look at Erlang pattern matching for tuples:

{Len, Status, Data} = {8, ok, "Trillian"}

Compare this with pattern matching elements of a tuple in LFE.

(set (tuple len status data) #(8 ok "Trillian"))


With that in our minds, we turn to Joe's matching example against a specific map element:

#{{color, red} := X1} = Z.

And we could do the same in LFE like this:

(set (map (update-pair (tuple 'color 'red) x1)) z)

I'm *really* uncertain about add-pair and update-pair, both the need for them and the names. Interested to hear from others who know how map is implemented in Erlang and the best way to work with that in LFE...

d

 
Reply all
Reply to author
Forward
0 new messages