Stream: ideas

Topic: Nice things about Ante


view this post on Zulip matklad (Jul 24 2022 at 19:41):

Another nice lil functional language I came across recently is ante: https://antelang.org/docs/language/. It is way bigger than roc, but it has a couple of tasteful design choices. My favorites were:

 map data (_ + 2)
    .filter (_ > 5)
    .max!
match foo
| Some bar -> print bar
| None -> ()

view this post on Zulip Richard Feldman (Jul 24 2022 at 19:58):

the |> is spelled . has interesting type system implications - I'm not sure how that could be implemented without either adding higher kinded types to the language or else giving up decidable principal type inference :thinking:

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:00):

e.g. if you put this into roc repl today, it infers that arg is a record which stores a lambda in its map field:

\arg -> arg.map

view this post on Zulip Brendan Hansknecht (Jul 24 2022 at 20:00):

If it requires a space before it, that shouldn't be the case, right?

view this post on Zulip Brendan Hansknecht (Jul 24 2022 at 20:00):

Or new line

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:00):

but it seems like in ante, arg is allowed to be an opaque type

view this post on Zulip Brendan Hansknecht (Jul 24 2022 at 20:00):

oh...interesting

view this post on Zulip matklad (Jul 24 2022 at 20:02):

https://antelang.org/docs/language/#member-access-traits

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:03):

interesting!

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:04):

how does that work with a function that takes the trait type as an argument?

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:04):

e.g. .map

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:05):

I guess it would generate a trait and then say one of the type variables has to have to have it? :thinking:

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:07):

that feels equivalent in power to higher-kinded types, but I could be missing something :big_smile:

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:11):

or if not, I'm not sure how .map would work in practice

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:13):

like if inside the same file, I wrote strings.map \before -> "\(before)!!!" and also nums.map \num -> num + 1

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:14):

is it possible that the type of .map could not be higher-kinded? 🤨

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:14):

well it's only dealing with first-kinds, I don't see how higher kinds play into this? As I understand it, x.f is syntax sugar (in Roc) for either x |> f or x |> .f where .f is the record access function \x -> x.f. So the point of ambiguity is which of those functions is available, but the type system can determine that either that's ambiguous or only one resolution exists

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:15):

right but this is for opaque types, not records

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:16):

which...I think makes a difference?

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:16):

maybe not?

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:19):

Oh I see. I guess the difference is, can the language find a unique resolution of .map for some type. But you don't need higher kinds for that

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:21):

Personally I don't think that . is easier to read than |>, I think the "is this a record access or dispatch to a function?" question that you may need to consider makes it more difficult to grok from the Roc perspective

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:21):

OTOH I could see how if your language does already have higher kinds, this provides a convenience

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:24):

tuples as syntax sugar for pairs

Tuple are coming to Roc soon! Not as an explicit syntax sugar for Pair x y though, if I recall correctly

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:50):

here's an example of what that style might look like in Roc: https://gist.github.com/rtfeldman/4d7cad972e77fc7cd68a816239a11c2b

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:51):

not sure how this would translate:

    response
    |> Str.split "\n"
    |> List.mapTry movieFromLine
    |> Task.fromResult

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:51):

I don't think it could be this:

    response
        .split("\n")
        .mapTry(movieFromLine)
        .fromResult()

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:52):

because fromResult would be associated with Task, which isn't involved in anything in the previous chain

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:52):

unless maybe it does the type resolution based on type inference, since the function is returning Task

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:52):

but if it's doing things like that, can you still have decidable principal type inference? :thinking:

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:52):

yeah, I think as long as you can infer the output type that would work

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:52):

yes

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:52):

huh, interesting!

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:53):

Well, I guess it depends how you're considering this feature

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:53):

so I guess then theoretically you could even convert

path = Path.fromStr "output.json"

to

path = "output.json.".fromStr()

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 20:54):

Like if split and mapTry and fromResult are in scope, from List, Result, and Task respectively, the answer is yes. But if all of those have to become abilities, it starts to become a bit wavier

view this post on Zulip Qqwy / Marten (Jul 24 2022 at 20:55):

I think using . as a binary operator in a functional language goes against the widely spread usage of . in existing functional languages for function composition, which is a right-to-left operation.

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:55):

well here it's more like . as in field access

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:55):

it's not like the . is its own operator, really

view this post on Zulip Folkert de Vries (Jul 24 2022 at 20:57):

it is in haskell

view this post on Zulip Qqwy / Marten (Jul 24 2022 at 20:57):

I see. It's the thing which is done in Ruby and Rust, where method dispatching happens on the thing that the result of the previous operation happens to be.

I work with a lot of this kind of code in my day job, and I have found it consistently harder to read than code that is explicit with the modules. (e.g. a pipeline such as in Elixir or current-day Roc). Especially in the presence of traits it is no longer obvious where to look for a particular function.

view this post on Zulip Folkert de Vries (Jul 24 2022 at 20:57):

is . as composition actually widespread?

view this post on Zulip Folkert de Vries (Jul 24 2022 at 20:58):

e.g. Clean uses the letter o for it

view this post on Zulip Folkert de Vries (Jul 24 2022 at 20:58):

and many newer languages pick >> and <<

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:58):

yeah agreed, Marten

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:58):

like compare:

    response
    |> Str.split "\n"
    |> List.mapTry movieFromLine
    |> Task.fromResult

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:58):

vs

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:58):

    response
        .split("\n")
        .mapTry(movieFromLine)
        .fromResult()

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:58):

one of these things is more self-documenting than the other :big_smile:

view this post on Zulip Richard Feldman (Jul 24 2022 at 20:59):

also more verbose, to be fair

view this post on Zulip Qqwy / Marten (Jul 24 2022 at 20:59):

Yes, that is the trade-off

view this post on Zulip Qqwy / Marten (Jul 24 2022 at 21:00):

Folkert de Vries said:

is . as composition actually widespread?

This is a good question to which I do not know the answer :blush:

view this post on Zulip Richard Feldman (Jul 24 2022 at 21:00):

another thing I prefer about |> is that the rules for when to use parentheses are more complex in the other style, unless you always require them (which is not ideal either)

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 21:01):

I don't think verbosity is a bad thing here. And the nice thing is it's flexible - as a Roc author you could import split, mapTry, and fromResult unqualified, which would be similar to the . access version in terms of verbosity

view this post on Zulip Richard Feldman (Jul 24 2022 at 21:01):

e.g. because you can omit parens for foo.bar baz but need parens for foo.bar() because otherwise it's ambiguous whether you're trying to access the bar function or call it

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 21:03):

also, to me, something like response.isError is quite ambiguous as a reader - is that trying to access the isError field on a response, or call isError response?

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 21:04):

I guess that could be solved with response.isError() though, nevermind

view this post on Zulip Richard Feldman (Jul 24 2022 at 21:04):

right, although that gets a bit weird with backpassing

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 21:04):

yeah

view this post on Zulip Richard Feldman (Jul 24 2022 at 21:05):

because you need to end lines in .await rather than .await() even though normally you'd add () to invoke the function

view this post on Zulip Ayaz Hafiz (Jul 24 2022 at 21:15):

as another perspective, I could see the .foo call syntax being confusing to new Roc developers, since that style is traditionally associated with dispatch over objects and OOP-like styles. But Roc doesn't have objects in the sense that Rust/Ruby/etc. do

view this post on Zulip Thomas Dwyer (Jul 24 2022 at 21:15):

Folkert de Vries said:

is . as composition actually widespread?

Haskell and friends use it to great success. See: point free functions, Lens. What i see here is |> is like Haskell's $application operator, not . function composition. I do not believe we should use . for the type system reasons/difficulties and because it would be confusing for programmers migrating from other pure functional langs.

view this post on Zulip Folkert de Vries (Jul 24 2022 at 22:06):

I mean the specific use of the dot character . to denote function composition. Many languages have function composition, but I think only haskell actually uses . as the "name" for that operator

view this post on Zulip Brendan Hansknecht (Jul 24 2022 at 23:05):

Would it not be this in Roc:

    response
        .Str.split "\n"
        .List.mapTry movieFromLine
        .Task.fromResult

view this post on Zulip Brendan Hansknecht (Jul 24 2022 at 23:10):

If we were directly converting from the |> syntax

view this post on Zulip Georges Boris (Jul 25 2022 at 10:20):

imo borrowing the concept of "objects with methods" from rust (even if just as syntax sugar for regular functions) would be a detractor from the simplicity you get from functional languages...

thinking about Elm, one of the best things about it is that there is virtually no syntax sugar magic so people learn the basics and - poof - everything just builds upon it.

as soon as we open this gate, people start to assume other magic are possible and so they lose the confidence in understanding everything they're seeing.


Last updated: Jun 16 2026 at 16:19 UTC