Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
My Favourite Gleam Feature (erikarow.land)
70 points by ingve on June 11, 2024 | hide | past | favorite | 39 comments


The discussion and reasons of "data first" vs. "data last" can be found by searching these terms (including type inference"). For example for Rescript (TAFKA Bucklescript) which changes OCaml's data last to a dáta first syntax. https://www.javierchavarri.com/data-first-and-data-last-a-co... which does explain the actual "technical" reasons behind the decision.


Thanks. This was really interesting. I hadn't considered the impact on type inference before.


I will say that it’s nice that you need explicit imports in Gleam. Elixir does not require this and a lot of code in our codebase suffers from unspecified imports which can be difficult to follow.


It’s a double edged sword.

After mucking around with import paths in so many languages for so long, I like that elixir is like “all the namespaces everywhere”


I thought I would love "all the namespace everywhere" (which would be a _great_ name for a tech talk at a conf, but it just feels too much like magic to me.

One thing I've never thought about, but you could potentially just make a single file with all the imports and that would make it easier to check, but it still just feels too much like magic and loss of control.


Doesn’t seem like magic to me.

I’d assume elixir looks at every file in the lib directory and compiles them. They all then get loaded into the beam vm with their respective module names


Oh absolutely,

But that isn't how I was using the term “magic”, I was using it as a generic descriptor similar to how the Zig language refers to things that are done in the background, or hidden, without control from the user. I prefer knowing everything a file requires, that way I never double up, or have too many deps without my knowledge of everything that is in a system.


I like the comparison to implicit methods or trait implementations in rust. However what about currying and partial application? The article didn't come back to that. It's that lost now in Gleam?

We wrestled with the same issue in PRQL. There we have "data last" in order to allow for currying and our pipe operator just pipes to the last argument so you still have the same pipeline syntax.

I also like the qualified imports. The lack of those was what turned me off Nim.


Gleam never had currying or partial application like with usual ML syntax.


Ok then you're not really giving up much with data-first and are gaining benefits.

Rye has an interesting approach with the * syntax to allow injecting into the second argument for op-words: https://ryelang.org/meet_rye/specifics/opwords/


These are all the same reasons to like Elixir.

For anyone that hasn’t tried writing Elixir style code with the pipeline operator should definitely check it out. It’s fun to iterate in the repl too, since you can build it sequentially without adding braces for nesting calls.


But elixir doesn't have types.


It does. Always has.

It just doesn’t have static types, it has strong types.

That difference gives you the flexibility to avoid circumstances where static typing can be a hindrance.


Is it really strong typing if you can't type functions parameters and returns?


"Strong typing" most of the time means "no implicit type conversion" like e.g. C or Javascript do. That is indepentend of having statíc or dynamic typing (and anything between).


The thing is, low level types can already be inferred by the operators anyway. That’s what dialyzer does and it catches most things.

For the rest of it, you have a conversation where the benefits of full static typing don’t exist in a distributed environment. Example, you’re making function calls to other nodes in the cluster and the node is updated while your code is running potentially changing the function call. You won’t get the chance to do a contract exchange live with other nodes, recompile and examine results.

Instead, you have to pattern match on the parts you care about just like with an API call to a server you don’t control.

Even with Gleam, static typing only protects your node. In a distributed environment you can’t validate everything and on the BEAM everything is a distributed environment.

Once you accept this, you realize that the benefit of static typing simply doesn’t exist here.


... and misses all the occasions where static typing can be a benefit.



Sure it does. How could it be dynamically typed if it didn't have types? How would you even program in a language that doesn't have any types?


Like in assembler. Or, if you prefer that, in untyped lambda calculus.


Now this is outside my comfort zone but I'd expect that to include integers.


Forth as well


assembler has tons of types. Bits, bytes, registers.


First release of Elixir with gradual typing announced today. It's not complete but the work is underway.

https://elixir-lang.org/blog/2024/06/12/elixir-v1-17-0-relea...


Everyone knows very well the parent comment means "static types", come' on.


No we don't. Many people are super confused about this.


No we're not (yes, it's a bad answer, but that's what you've just done).


Hello Many People. I am boxed.


Many argue that static types and Beam style of deployments are actually a bad fit. Static types assume atomic deployment of a statically checked codebase for their guarantees.


It doesn’t right now, but it will. Jose and company have been working on a gradual type system. Elixir 1.17 recently released the first typing features.


You seriously think the language doesn't differentiate between strings and integers let alone all the other types of values in elixir?


So you're saying it's even better?


The big problem is that order is a thing at all. Positional arguments are the mistake, any discussion based on that mistake is garbage-in-garbage-out. Partial application should be based on named arguments, just like all functional calls (except some very specific and rare exceptions).


I use a similar pattern in my typescript code by using the Remeda library[0]. Like the patterns described in the article, the util functions can be used in both a one-off data-first way:

    R.map(arr, (x) => x + 1)

and as part of a pipe in a data-last way:

    R.pipe(
      [0, 1, 2, 3, 4, 5, 6],
      R.map((x) => x + 1),
      R.filter((x) => x % 2 === 0),
      R.take(1)
    )

I've really enjoyed using it and would recommend it to anyone who would like to write their typescript in a more functional way.

[0] https://remedajs.com/


Since it wasn't mentioned, Elm uses the pipe operator to place the previous value into the last position instead of the first. The best of both worlds?


OCaml and F# do the same!


As someone just getting into gleam, and without much functional programming experience, this article really clearly explained something that I hadn't really considered the significance of. Makes me appreciate the pipe syntax all the more now!


The Haskell / Erlang approach is a natural fit for the pipe operator that enables pipeline style (see F#, OCaml).

Having the arguments order flipped means that currying and pipeline do not play nicely together.

I don’t see how the Gleam approach is better.


Does Gleam lean on e.g. Ocaml like Elixir leans on Ruby or?




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

Search: