Stream: ideas

Topic: Switch function types to use vertical bars


view this post on Zulip Joshua Warner (Dec 27 2024 at 15:02):

To go along with replacing backslash closure syntax with ||-delimited closure args, what if we also did the same to types?

i.e. instead of writing Str, Str -> Str, you write |Str, Str| Str.

That brings a nice syntactic symmetry with the implementation:

foo: |Str, Str| Str
foo = |a, b| "Hello ${a} and ${b}"

And, as a side-note, it resolves a long frustrating part of the parser: parsing function types in tuples / tag unions / records. When done with lookahead and/or backtracking (as is currently done), this can lead to exponential behavior in the parser. It's possible to parse without backtracking, but the parser gets rather messy.

view this post on Zulip Richard Feldman (Dec 27 2024 at 15:11):

we definitely need -> or => in the type! :big_smile:

view this post on Zulip Sam Mohr (Dec 27 2024 at 15:13):

I think the arrow still works with this:

foo: |Str, Str| -> Str
foo = |a, b| "Hello ${a} and ${b}"

view this post on Zulip Georges Boris (Dec 27 2024 at 15:14):

Just for my OCD:

foo : |Str, Str| -> Str
foo = |a, b| "Hello ${a} and ${b}"

view this post on Zulip Richard Feldman (Dec 27 2024 at 15:17):

this is an interesting idea to me for a different reason than the ones discussed so far: the current function type syntax doesn't have an obvious way to represent a zero-arg function, but this actually would!

view this post on Zulip lue (Dec 27 2024 at 15:17):

can someone get me up to speed why the ! at the end of the name is not enough already to signify side effects? (I'm guessing for stuff like standalone types)

view this post on Zulip Anthony Bullard (Dec 27 2024 at 15:18):

lue said:

can someone get me up to speed why the ! at the end of the name is not enough already to signify side effects? (I'm guessing for stuff like standalone types)

Yeah, exactly that.

view this post on Zulip Richard Feldman (Dec 27 2024 at 15:18):

yeah, like for example anonymous functions and types of functions that take other functions as arguments

view this post on Zulip Brendan Hansknecht (Dec 27 2024 at 16:47):

I think |...| -> ... could be nice

view this post on Zulip Richard Feldman (Dec 27 2024 at 21:48):

if we do that, I realized they actually can be nested in types without parens

view this post on Zulip Richard Feldman (Dec 27 2024 at 21:49):

map : |List a, |a| -> List b| -> List b

view this post on Zulip Richard Feldman (Dec 27 2024 at 21:50):

because | only ends the args list if it's followed by either -> or =>

view this post on Zulip Richard Feldman (Dec 27 2024 at 21:50):

otherwise it's opening a nested function type

view this post on Zulip Richard Feldman (Dec 27 2024 at 21:52):

although I don't love how that looks :laughing:

view this post on Zulip Brendan Hansknecht (Dec 27 2024 at 21:56):

Oh yeah....didn't think about nesting...works but is not as nice as parents for readability

view this post on Zulip Anthony Bullard (Dec 27 2024 at 22:04):

Anything nested should have proper open/close brace pairs as delimiters

view this post on Zulip Richard Feldman (Dec 27 2024 at 22:10):

map : |List a, (|a| -> List b)| -> List b

view this post on Zulip Richard Feldman (Dec 27 2024 at 22:13):

today:

map : List a, (a -> List b) -> List b

view this post on Zulip Richard Feldman (Dec 27 2024 at 22:16):

today definitely looks nicest to me :big_smile:

view this post on Zulip Richard Feldman (Dec 27 2024 at 22:16):

for higher order function types

view this post on Zulip Georges Boris (Dec 27 2024 at 22:42):

If only the lambda syntax could also match the current type annotations... oh wait :grimacing:

view this post on Zulip Richard Feldman (Dec 27 2024 at 22:52):

there is the question of how the current syntax would represent the type of a function with no arguments

view this post on Zulip Richard Feldman (Dec 27 2024 at 22:53):

one possibility:

run! : => {}
run! = ||
    # ...

view this post on Zulip Agus Zubiaga (Dec 27 2024 at 23:30):

Why don’t we just do parens around args? It would match the calling syntax

map : (List a, a -> List b) -> List b

run! : () => {}

view this post on Zulip Georges Boris (Dec 27 2024 at 23:30):

Tbh I don't dislike that. It may look unexpected the first time but I don't see how this could be interpreted as anything other than 0-arg function.

It would be great to actually ask someone.

# this means a function that takes two numbers and returns a number
fn : Num, Num -> Num

# this one takes one number and returns a string
fn : Num -> Str

# what does this mean?
fn : -> Str

view this post on Zulip Richard Feldman (Dec 27 2024 at 23:33):

Agus Zubiaga said:

Why don’t we just do parens around args? It would match the calling syntax

map : (List a, a -> List b) -> List b

run! : () => {}

in that design, wouldn't the first one need nested parens?

map : (List a, (a) -> List b) -> List b

view this post on Zulip Agus Zubiaga (Dec 27 2024 at 23:33):

True

view this post on Zulip Agus Zubiaga (Dec 27 2024 at 23:34):

Looks fine, though

view this post on Zulip Georges Boris (Dec 27 2024 at 23:37):

imho the extra noise for 99% of type annotations doesn't outweigh the perhaps weird first of impression of fn : => {}

view this post on Zulip Richard Feldman (Dec 28 2024 at 02:02):

another scenario that doesn't look great with bars in the types: multiline args :sweat_smile:

parse_user_id :
    |
        Request,
        Jwt.Secret,
        Instant,
    | -> Result UserId [MissingTokenHeader, InvalidJwt Jwt.ParseErr]
parse_user_id = |req, jwt_secret, now|

view this post on Zulip Richard Feldman (Dec 28 2024 at 02:03):

with the parens idea it could be:

parse_user_id : (
    Request,
    Jwt.Secret,
    Instant,
) -> Result UserId [MissingTokenHeader, InvalidJwt Jwt.ParseErr]
parse_user_id = |req, jwt_secret, now|

view this post on Zulip Richard Feldman (Dec 28 2024 at 02:04):

today:

parse_user_id :
    Request,
    Jwt.Secret,
    Instant
    -> Result UserId [MissingTokenHeader, InvalidJwt Jwt.ParseErr]
parse_user_id = |req, jwt_secret, now|

view this post on Zulip Sam Mohr (Dec 28 2024 at 02:04):

Today looks great!

view this post on Zulip Sam Mohr (Dec 28 2024 at 02:05):

I'd only lean towards circle brackets for alignment with our function defs, but since they're different brackets, I don't think its worth it

view this post on Zulip Karl (Dec 28 2024 at 06:19):

parse_user_id :  |
    Request,
    Jwt.Secret,
    Instant,
    | -> Result UserId [MissingTokenHeader, InvalidJwt Jwt.ParseErr]
parse_user_id = |req, jwt_secret, now|

view this post on Zulip Kilian Vounckx (Dec 28 2024 at 08:43):

Parens make it look like the function takes a tuple to me


Last updated: Jun 16 2026 at 16:19 UTC