Hacker News new | past | comments | ask | show | jobs | submit login

What is the benefit of having three ways to write strings?

        \a  ;; compiles to "a"
	:a  ;; compiles to "a"
	"a" ;; compiles to "a"
This is not meant as destructive criticism; I'm genuinely interested in the motivation for that.



To expand on others' answers: In Clojure, \a is a Java character, :a is a Clojure keyword, and "a" is a Java string.

    user=> (type \a)
    java.lang.Character
    user=> (type :a)
    clojure.lang.Keyword
    user=> (type "a")
    java.lang.String
The only unusual thing is the keyword. Fogus and Houser say this: "Because keywords are self-evaluating and provide fast equality checks, they're almost always used in the context of map keys." But here you're back to checking equality of strings. Not sure if the same is true for ClojureScript.

You can also use them as functions to look up values in maps. For example:

    user=> (:my-key {:other-key 1, :my-key 2})
    2
There are other places they're used--for example, list comprehensions with `for`:

    (for [x (range 10)
          y (range 10)
          :when (= x y)]
      [x y])
    => ([0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9])
In Clojure keywords are nice to have.


Seems like a disadvantage to me. It's mostly just confusing and the only thing it affords is the ability to use :keywords as functions (:like so) which is just sugar for property access, so["like"]. This is broken, though.

The coolest thing about :keywords in Clojure is that they really are functions and you can do things like (map :keywords ontoSomething) to extract the same property out of many things. It don't work in Wisp.


What you're not realizing, though, is that each time you use the string "like" as a key for a map/dictionary, you're allocating a new object on the heap. So what if you have many keys, and they're all strings? What if you have many maps that share the same keys? That's a lot of memory to allocate, and strings are an extremely inefficient way to implement what is essentially a unique identifier to address map values. Instead, use an integer as a unique identifier.

That's basically what keywords do -- they are human-readable and compile down (prob. using some hash) into some integer, and the integer is stored globally only once. The biggest savings in memory comes when using maps, and that's how & why keywords get their name.


Javascript strings are immutable and literals are almost certainly preallocated and stored in a table of constants. Why should using a string keys cause heap allocations?


If you have a VM where strings are immutable and interned, you're right. Java doesn't guarantee that strings are interned (though you can ask the VM to intern it), so Clojure treats them differently.

Also, Java declared java.lang.String as final, so Strings can't be extended to implement clojure interfaces to get nice behavior, like IFn and IKeywordLookup.


The coolest thing about :keywords in Clojure ... don't work in Wisp.

Ok. That sucks. So pretty much anyone already a bit serious about Clojure wont be enticed by this then.

Edit: From the page:

    ;;    Keywords can be invoked as functions, that desugars to plain
    ;;    associated value access in JS
    (:bar foo) ;; => foo["bar"]
That seems pretty close to what's expected though, doesn't it?


I'd expect `(:bar foo)` to desugar to `foo["bar"]()` since the keyword is being used as a function. Property access in clojurescript uses the ugly `(.-bar foo)` syntax.


No, to get foo["bar"]() in CLJS you write ((:bar foo)). :bar in this case is a function, which returns a value by a key ":bar" from "foo", it's not a property.


Those are 3 distinct types in Clojure; unfortunately they have no equivalents in JavaScript, thus they become strings.


To my knowledge, keywords are actually functions in Clojure, which do have an equivalent in JS. "Looks more like Clojure" seems like a good enough reason to me.


Keywords implement many interfaces, function (IFn) being one of them. See this line in the clojure source code:

  public class Keyword implements IFn, Comparable, Named, Serializable, IHashEq 
https://github.com/clojure/clojure/blob/master/src/jvm/cloju...


They can be used as functions, but I think they're implemented differently for optimization. Could be wrong.


I'm not sure because I don't know anything about this language or clojurescript, but I do know clojure and I'll explain the differences in terms of that.

    \a ;; becomes 'a' in java.  The character of 'a'
    :a ;; a keyword of :a. More efficient than using 'a'.  Also it can act as a function.  If you have a map with a key of :a in it, instead of having to say "get me the value for :a" you can say (:a map)
    "a" becomes "a" in java.  The string of "a"


In clojurescript strings are just sequences of characters where characters are expressed as \a and strings as "foo". In JS single char string is an equivalent of a character, as a side effect \a and "a" compiles to a same thing, which BTW matches behavior of clojurescript as well. Main reason for \a like character types existence is clojure(script) compatibility and nothing more.

As of :a keyword it's a different story. As a matter of fact actual (quoted) keyword ':a does not compiles to "a" string, but general references do. That is because in JS constant string literals like "load", "DOMContentLoaded" are used in cases where idiomatic Clojure(script) would have used :load :DOMContentLoaded keywords, there for it made sense to just compile those keywords to semantical analogues in JS, same as `foo-bar` lisp naming convention compiles to adequate `fooBar` naming in JS.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: