Stream: beginners

Topic: roc's typeclass alternative


view this post on Zulip Thomas Dwyer (Mar 12 2022 at 03:05):

As i come from Haskell, typeclasses seem crucial for proper polymorphic functions. I read the roc for elm programmers file, and the Num type seems much like Haskell's Num, but all of the instances are just phantoms holding some a.

  1. How do the phantom types work for Num's "instances" (is there a proper term for these aliases?)
  2. Is it possible for the programmer to create something akin to Num and create those style of "instances"?
  3. If #2 either isnt possible or not idiomatic, what is? how does one limit the types that a polymorphic function can accept? overloading like in Java exists, but is that really the only good way?

view this post on Zulip Brendan Hansknecht (Mar 12 2022 at 03:13):

@Folkert de Vries or @Ayaz Hafiz are probably most qualified to answer especially in the type theoretical sense, but I think I know the general answer.

Currently, Num a is special. It is built into the compiler with special restrictions that a Num a must be either a Float a or Int a. Those must then become specific types F32, I8, etc.

Currently if I were to make Foo a that a could be anything at all. I have no way to constrain it. (well I could make it Foo (Num a), but that is just relying on Num a).

In the future this will be changing. We will be adding abilities. You can think of abilities as java interfaces or rust traits or something of that nature. They enable me to say Foo a where a supports Equality. Now that a must be a type that has ==. This will enable regular users to define something akin to Num a.

More info on abilities in this chat: #ideas > Abilities

view this post on Zulip Richard Feldman (Mar 12 2022 at 03:39):

@Thomas Dwyer check out this issue!

view this post on Zulip Thomas Dwyer (Mar 12 2022 at 05:25):

very interesting. i have a few comments on the problems and various discussions (perhaps this belongs in a different thread?)

syntax. the qualification of abilities at the end feels weird. I see (Num a) as a preface to the type rather than a clarification at the end. this is a minor gripe with the syntax though. perhaps something like f : a is (Addable, Divisible, Multiplicative) in a, a -> a. really, i just find it at the end to be weird. maybe it just takes some getting used to.

functionlessness in docs. this is a hard one. the "it wasnt before but now is" could be extended to give examples of usages which are now broken and ways to mitigate them in a seemless fashion. in documentation, there could be a section on function attributes? i imagine on the side there would be stuff like asymptotics and the usual with some other miscellanea, which seems like it would fit right into.

custom arithmetic operators. my comment on this is more a side comment. i think we should avoid an overarching Num ability that requires (+), (-), (*) etc. These should definitely be split into their own components. I say this because many things would like to have the ability to be concatenated, but wouldnt make sense to have a division function or even worse some square root function. there could be ability tags, where one can define Num a : [Addable a, Subtractable a, ...]* as an alias like how haskell's -XConstraintKinds allows for kind aliases; i kinda like this approach, as it doesnt seem like a bunch of complexity to an existing system, and the syntax is nearly identical to current tag aliases.

view this post on Zulip Richard Feldman (Mar 12 2022 at 13:29):

functionlessness

oh I need to revise the docs - that was before the Abilities design...functionless is not a thing anymore post-Abilities!

view this post on Zulip Richard Feldman (Mar 12 2022 at 14:04):

regarding the Num ability - we talked about that and I think Num is the way to go.

I agree with James Gosling when he decided not to support operator overloading in Java based on how he saw it being used in C++, and although I want custom numeric types to exist (e.g. a user-defined Fraction or Complex type, or numbers with units of measure), I think it's a step in the wrong direction to have + being used in arbitrary DSLs or overloaded for something like concatenation of strings or other collections.

I think it makes it much clearer that arithmetic operators should only be used for numbers if it's required to implement all the numeric operations in order to use them!

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 15:19):

After some thought I'd axe infix + - / * operators all together!

People are used to write mathematical operations, but this is not strictly math, it's programming which start's pretty low level (you have to choose between I32 and I64, which is by it self not a math problem)

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 15:22):

I think they introduce ambiguity that is hard to get rid off

view this post on Zulip Brendan Hansknecht (Mar 12 2022 at 15:35):

I think they introduce ambiguity that is hard to get rid off

What specially are you thinking of?
Operating ordering? Other?

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:04):

Mostly interplay of Number like things that we have built-in, abilities and number like things that people might want to build eg. matrices.

view this post on Zulip Richard Feldman (Mar 12 2022 at 18:06):

I think the percentage of people who wouldn't use Roc exclusively because it didn't have infix math operators is unacceptably high

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:06):

If we remove them, we set the expectations. No infix things.

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:06):

I know that assumption, but I'd like to measure it.

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:07):

Like, if there is a reason to be hyped for other things about Roc: platforms, purity, sexiness, I mean speed ...

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:08):

I don't know, I totally get it, but at the same time, people are spoiled.

view this post on Zulip Richard Feldman (Mar 12 2022 at 18:08):

I get where you're coming from because I've had similar thoughts myself over the years :big_smile:

view this post on Zulip Richard Feldman (Mar 12 2022 at 18:09):

it would be nice if we had all grown up being taught arithmetic using prefix function calls instead of infix symbols, but unfortunately... :sweat_smile:

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:09):

you know, if you enumerate all the great thing that language has to offer, and like, yeah you are calling a function to add two numbers, because math operations cost too.

view this post on Zulip Richard Feldman (Mar 12 2022 at 18:10):

it's the wrong place to spend our weirdness budget

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:10):

I know. We first publish a book!
Infix Math is Backwards :)

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:10):

:D

view this post on Zulip Richard Feldman (Mar 12 2022 at 18:10):

:joy:

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:11):

I agree, just this whole subject wouldn't exist the axe chopped at that place. :)

view this post on Zulip Martin Stewart (Mar 12 2022 at 18:11):

Since Roc will have an AST based IDE, couldn't it in theory have a mode where all the infix math is replaced with the underlying add/sub/div/etc function calls?

view this post on Zulip Richard Feldman (Mar 12 2022 at 18:13):

yep! Definitely possible

view this post on Zulip Zeljko Nesic (Mar 12 2022 at 18:13):

Yes

view this post on Zulip Thomas Dwyer (Mar 15 2022 at 11:29):

on this topic, i was using roc and was wondering, with no HKTs, how would roc express something akin to Haskell's functor? is it really necessary for each type to define its own map? it doesnt seem very easy to write generic code to map over something that can be mapped over. functors arent even particularly complicated, but it is very useful. and what about Traversable and Foldable? i know list has List.walk, but what about a generic walk?

view this post on Zulip Folkert de Vries (Mar 15 2022 at 11:32):

the tradeoff we make is to keep the language simple and not have the (inevitable, if you add HKTs) functor/applicative/monad hierarchy. It hurts a bit, but elm proves that it's fine in practice

view this post on Zulip Thomas Dwyer (Mar 15 2022 at 11:34):

i understand the simplicity argument, im just confused what the alternative is for this kind of expression. what if i want a function to work on anything mappable? or anything foldable?

view this post on Zulip Folkert de Vries (Mar 15 2022 at 11:46):

the idea is that in practice you don't. List is effectively the common denominator of data structures, it is what most api's would use. There are particular cases where a Set or Dict is useful, they come with a full and specific api (we've had a whole thread on what ordering guarantees such a structure must have. e.g. Set is not a lawful functor)

view this post on Zulip Folkert de Vries (Mar 15 2022 at 11:47):

wait is it a functor? it's not a monad, I know that

view this post on Zulip Folkert de Vries (Mar 15 2022 at 11:48):

ah here we go https://www.reddit.com/r/haskell/comments/2090x3/ask_rhaskell_why_is_there_no_functor_instance_for/

view this post on Zulip Thomas Dwyer (Mar 15 2022 at 11:59):

didnt think about it that way, i guess it makes sense since recursive ADTs are less common in Roc than Haskell, so there is more emphasis on the actual underlying representation rather than an abstract tree of types. given that i can see why its not particularly necessary. im guessing this would also aid in efficiency, since the computer likes arrays more than a node with pointers?

view this post on Zulip Folkert de Vries (Mar 15 2022 at 12:01):

yes absolutely that is one of the major reasons to choose an array as the underlying representation

view this post on Zulip Folkert de Vries (Mar 15 2022 at 12:02):

though to be fair we can only do that because we have opportunistic in-place mutation

view this post on Zulip Narek Asadorian (Oct 20 2023 at 17:05):

I wanted to ask a similar question to this thread from a year ago... if abilities are a language feature in Roc, why not go whole hog on abilities and abstract over kinds?

view this post on Zulip Narek Asadorian (Oct 20 2023 at 17:10):

E.g. I always have this gripe with Rust's stdlib.. what is bind on the effect type I'm working with? It's called "flat_map" on Vec/Iterator, "and_then" on Result, (~await + ?) on Future, and so on. It just feels more organized and ergonomic to unify semantic concepts under canonical terminology.

view this post on Zulip Narek Asadorian (Oct 20 2023 at 17:11):

Definitely understand that the category theoretic hierarchy from Haskell is arcane to the average developer

view this post on Zulip Brendan Hansknecht (Oct 20 2023 at 17:39):

Someone may have a better answer, but from my view, the question is reversed:

What do we gain from making abilities more complex and adding higher kinded types?

Relatedly, are those gains worth it and do they fit the language well? Those features add to the language complexity, compiler complexity, and increase overall learnability burden.

Features have to earn a place to be worth adding to the language. Abilities happened to pass that threshold due to a few specific use cases (like serialization). As such, they got a minimal implementation to meet those specific use cases.

We want a clean and simple language, so generally we avoid adding features (especially large ones) until there is a compelling reason.

view this post on Zulip Narek Asadorian (Oct 20 2023 at 17:53):

This is a great answer. I've been trying to deduce what the language's design philosophy is outside of the obvious (be functional, embed easily, performance) and this explains it well.

view this post on Zulip Richard Feldman (Oct 20 2023 at 18:09):

also here's a related FAQ entry! https://github.com/roc-lang/roc/blob/main/FAQ.md#higher-kinded-polymorphism

view this post on Zulip Narek Asadorian (Oct 20 2023 at 21:02):

@Richard Feldman As a veteran of the Scala Wars :tm: I get this


Last updated: Jul 06 2025 at 12:14 UTC