I think there are two key parts to the syntax. How let!
looks and how :=
looks. This proposal defends those choices and goes a bit farther with them.
I like let!
because:
- It is minimally invasive — when you have a let-macro nested in a case expression, it is going to be a pain to write out something like
with Task.task let ...
every single time you want this. Imagine replacing all occurrences ofdo
in Haskell code with 10+ characters. - The connection to
let
is obvious — it is obviously some weird kind of let, no extra concepts at all. - It encourages two space indent — now that I see it, I am thinking about using 2-space indent in all of my let expressions. This is a very OCaml thing to do (common practice there) and I think it looks quite nice.
- It guides how we specify which macro to use — another variation of this syntax that I considered was
let{maybe}
orlet(maybe)
orlet[maybe]
and all of these (1) overlap with existing syntax in some way and (2) make it feel nicer to give something besides a variable or qualified variable.
On point 4, I want to show an example:
add maybeLeft maybeRight =
let! (Macro.simple Maybe.succeed Maybe.andThen)
x := maybeLeft
y := maybeRight
in
Just (x + y)
add maybeLeft maybeRight =
let(Macro.simple Maybe.succeed Maybe.andThen)
x := maybeLeft
y := maybeRight
in
Just (x + y)
The second one feels nicer to me, which I think is a bad thing. Writing super crazy macros inline does not seem too nice to me, and I think the let!
syntax makes it feel dirtier. Maybe if we can find good/valid examples of inline macro definitions this changes.
I like :=
because:
- I think there are already too many arrows in Elm. Types go -> and records go <- and cases go -> and it ends up looking a bit wild. In a moment I'll share a proposal to get rid of more arrows :)
- Overall the cultural meaning seems to be "do something special" which I think is as specific as we want to be for a macro system. It is used in OCaml for working with references. It reminds me of old school languages that use it for assignment. It is used in Go for "special kind of assignment". Overall, it feels imperative and indicates something odd is happening.
I think it kind of sucks that :=
kind of looks like :
but that's the biggest complaint I have.
What if we rename record update to =
and record extension to :=
to be parallel with let macros? Here are some examples:
point =
{ x = 3, y = 4 }
point' =
{ point | x = 5 } -- { x = 5, y = 4 }
point3D =
{ point | z := 5 } -- { x = 3, y = 4, z = 5 }
The idea is that we use record update all the time but we have this kind of weird update symbol. We basically never ever use record extension, but it has the super nice =
symbol.
I would interpret things here as :=
is for when you want to update the record in a crazy special way. Same as with let macros, "I want to get this value in some special way".