Functional Programmers need to take a look at Zig
44 points
2 hours ago
| 8 comments
| pure-systems.org
| HN
drgiggles
11 minutes ago
[-]
It’s possible (even true in my opinion) that garbage collected functional languages and low level languages like Zig are both great, and serve different purposes. I actually ship stuff in Haskell believe it or not. I also think Zig is very cool and have played around with it quite a bit. Yes, garbage collection hurts performance, but the reality is that the overwhelming majority of all software does not suffer from the performance loss between well written code in a reasonably performant functional gc language and a highly performant language with manual memory management. It’s just not important. But not having to deal with the cognitive overhead of managing memory and being able to deal in domain specific abstractions only is a massive win for developer productivity and code base simplicity and correctness. I think OxCamls approach of opting in to more direct control of performance is interesting. I also think it’s great that many functional patterns are making their way into imperative first languages. Language selection is always about trades offs for your specific use case. My team writes Haskell instead of Rust because Haskell is plenty fast for our use case and we don’t have to write lifetime annotations everywhere and think about borrowing. If we needed more performance we would have no choice but to explore other languages and sacrifice some developer experience and productivity, that’s very reasonable. I’m also not saying performance doesn’t matter (if you’re writing for loops in Python, stop). But this read to me like “because better performance exits with manual memory management, all garbage collectors are bad, so I’ll force zig to be something it’s not in order to gain performance I probably don’t need”. Which to me is an odd take. A more measured way of thinking about this might be, it can be useful to leverage functional patterns where appropriate in low level languages, if you find yourself needing to write code in one.
reply
dnautics
1 hour ago
[-]
io is not a monad. theres nothing stopping you from stashing a global io "object" and just passing the global wherever you interface with the stdlib.

It's dependency injection. and yes, you can model dependecies like a monad but most people, even in less pure fp langs, don't.

i don't really say this to just be a pedant, but if you're an fp enjoyer, you will be disappointed if you get the picture that zig is fp-like, outside of a few squint-and-it-looks-like things

reply
tux1968
1 hour ago
[-]
My reading of the article, was that the author seems to be in search of a new paradigm, that moves beyond what he sees as the limitations of "fp-like" languages as they exist today. His point appears to be that Zig provides the benefits of "fp-like" languages that exist today, while avoiding at least some of the downsides.

And he does admit you may have to squint, to appreciate the fp capabilities provided by Zig.

reply
danieltanfh95
12 minutes ago
[-]
I am not even sure if its a general pattern (inject any dependency?) or a specific pattern they added to Zig
reply
dnautics
3 minutes ago
[-]
idk in elixir we basically do exactly whats happening with io parameters when mocking or swapping implementations that all satisfy the same behaviour.
reply
continuational
51 minutes ago
[-]
Do you really prefer this:

  fn Maybe(comptime T: type) type {
    return union(enum) {
        value: T,
        nothing,

        const Self = @This();

        pub fn just(the_val: T) Self   { return .{ .value = the_val }; }
        pub fn nothing() Self          { return .nothing; }

      }
    }
Over this?

    data Maybe a = Just a | Nothing
reply
rene_d
45 minutes ago
[-]
Optionals handle this in zig:

  var value: ?T = null;
Write:

  value = 10;
Read:

  if (value) |x| x+=1
reply
continuational
35 minutes ago
[-]
Sure, but this is an example from the article, and pertains to sum types in general, not just Maybe.
reply
dnautics
10 minutes ago
[-]
i dont think its generally a good idea to be making complex type generators like this in zig. just write the type out.

the annoyingness of the thing you tried to do in zig is a feature. its a "don't do this, you will confuse the reader" signal. as for optional, its a pattern that is so common that it's worth having builtin optimizations, for example @sizeOf(*T) == @sizeOf(usize) but @sizeOf(?*T) != @sizeOf(?usize). if optional were a general sum type you wouldn't be able to make these optimizations easily without extra information

reply
nesarkvechnep
41 minutes ago
[-]
Came to say this. Early in my career I really thought implementing Maybe in any language is necessary but not I know better. Use the idioms and don’t try to make every language something it’s not.
reply
eikenberry
43 minutes ago
[-]
This looks like an example of a low level language vs a high level language (relatively speaking). The low level language makes a lot more of what is going on underneath explicit compared to the higher level language which abstracts that away for a common pattern. Presumably that explicitness allows for more control and/or flexibility. So apples to oranges?
reply
continuational
37 minutes ago
[-]
I don't think so, where's the extra information in the Zig example?

In Rust, which is arguably also a low level language, it looks like this:

    enum Option<T> {
        None,
        Some(T),
    }
reply
foltik
12 minutes ago
[-]
Low-level doesn’t mean more information, it means more explicit.

In Zig, that means being able to use the language itself to express type level computations. Instead of Rust’s an angle brackets and trait constraints and derive syntax. Or C++ templates.

Sure, it won’t beat a language with sugar for the exact thing you’re doing, but the whole point is that you’re a layer below the sugar and can do more.

Option<T> is trivial. But Tuple<N>? Parameterizing a struct by layout, AoS vs SoA? Compile time state machines? Parser generators? Serialization? These are likely where Zig would shine compared to the others.

reply
rdevilla
47 minutes ago
[-]
My old memories of Guava in Java 6 have been triggered.
reply
jstanley
58 minutes ago
[-]
> Noise is anything that must be written for the program to function that is not relevant to the domain.

> ...

> What facilities does the language provide me to create correct-by-construction systems and how easily can I program the type-system.

Isn't programming the type-system orthogonal to the program's domain in the same way that manual memory management is?

reply
rdevilla
48 minutes ago
[-]
No? I don't agree. The domain can be strongly modelled in the types; for instance, declaring kilometers, seconds, etc. instead of using primitive floats/reals everywhere, to statically prevent dimensional analysis issues.
reply
NordStreamYacht
33 minutes ago
[-]
I'm still fighting with Elixir and losing - for some reason I can't get my head around all the slightly different ways to initialise stuff.
reply
dnautics
20 minutes ago
[-]
"slightly different ways to initialise stuff."

can you elaborate? theres only what 11 datatypes in elixir?

reply
voxl
47 minutes ago
[-]
A functional programmer who casts away proper sum types and pattern matching is no functional programmer at all
reply
rgoulter
28 minutes ago
[-]
I thought lisps were all functional programming, and lack sum types and pattern matching?

In which case, what's the term for the "proper sum types and pattern matching" flavour of things?

reply
rienbdj
2 minutes ago
[-]
(Pure) expression orientation is the true marker of FP
reply
pyrolistical
53 minutes ago
[-]
I don’t get it

Why write:

EqPoint.eql(a, c)

When you can write:

Point.eql(a, c)

reply
givemeethekeys
44 minutes ago
[-]
Isn't the whole point of abstraction to not care about whats underneath unless you really have to? But ideally, you don't because the abstraction is "good enough"?

I haven't heard anyone writing code in Elixir complain about performance issues.

reply
nesarkvechnep
39 minutes ago
[-]
What’s up with the last paragraph? Nobody is complaining because the BEAM is good enough for the typical use case?
reply
dnautics
14 minutes ago
[-]
because you're not reaching for elixir when you need performance.

btw we do sometimes bitch about performance :)

reply