Stream: beginners

Topic: Methods


view this post on Zulip Philippe (Mar 21 2022 at 12:15):

Is there an official reason why roc doesn't support methods?

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

as in, functions associated with objects?

view this post on Zulip Brian Carroll (Mar 21 2022 at 12:22):

Different languages use the word "method" to refer to different things so it would be helpful to know which you mean. It often refers to functions attached to an object, which is an object-oriented concept and Roc is a functional programming language that doesn't have object orientation. Functional languages deliberately separate functions from data.
Rust has a different concept of trait methods.

view this post on Zulip Philippe (Mar 21 2022 at 13:36):

Just a question I received on Reddit.

I forgot the FP nature of Roc (of course it is, but slipped my mind).

I responded more in line of consistency. "Methods or just functions with a self argument" and "pipe operator allow chaining".

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

So I looked at the reddit post and I find adhoc polymorphism an interesting goal. iiuc, they want it so that someone could say dict.insert k v. The major thing hidden here from regular Roc is the implementation of the dictionary. I could theoretically use that every time I use a dictionary even if one is array backed and another is actually hashing.

Currently in Roc, if I wanted to that in the same module, I couldn't write Dict.insert dict k v in both locations. I would have to write FlatDict.insert in one location and HashDict.insert in the other.

view this post on Zulip Brendan Hansknecht (Mar 21 2022 at 15:38):

The other goal sounds like it would be a direct alternative to pipeline syntax via function chaining. E.g:

dict.insert k0 v0
    .insert k1 v1
    .insert k2 v2

Which as I typed I realized has a major problem with parsing due to how args and functions are grouped together.

view this post on Zulip Kevin Gillette (Mar 21 2022 at 15:48):

so would Dict be an ability in that case, leveraging builtins like dictInsert?

view this post on Zulip Kevin Gillette (Mar 21 2022 at 15:50):

It seems like part of the value in non-default dict implementations is the exposure of novel capabilities or semantics. Performance is another reason for them, but performance is often obtained through those novel access approaches

view this post on Zulip Brendan Hansknecht (Mar 21 2022 at 15:58):

If we exposed Dict as an ability, I guess we could make any dictionary use Dict.insert.

In this cases, I was assuming Dict was just the builtin dictionary and that a user was potentially using other dictionaries that happen to use the same API.

view this post on Zulip Brendan Hansknecht (Mar 21 2022 at 16:00):

Imagine any set of types that have different implementations but would likely have the same interface.

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:10):

abilities are ad hoc polymorphism, same as Rust traits

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:12):

as for the syntax, foo.bar baz already means that foo is a record, it has a bar field which holds a function, and foo.bar baz calls that function passing baz

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:13):

also, that calling style is the reason Rust has errors of the form "this doesn't compile...did you forget to import a trait maybe?"

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:14):

fully-qualified calls don't have that problem, which is one reason abilities in Roc are designed to be called that way :big_smile:

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:14):

e.g. Bool.isEq : a, a -> Bool | a supports Equating

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:15):

the a supports is the ad-hoc polymorphism; the code for isEq changes based on the implementation provided in the definition of a - so it's dispatching to a completely different implementation based on the type

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:15):

but you still call it like a normal function, e.g. Bool.isEq foo bar

view this post on Zulip Brendan Hansknecht (Mar 21 2022 at 16:16):

Sounds about right. Was just trying to give context on the question. I guess this does open questions around the standard library types potentially being abilities as well so that it is easy to make user land drop in replacements.

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:16):

so as long as Bool is imported, we know where to find the definition of isEq

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:16):

in contrast, if we had foo.isEq bar then it's not immediately clear which module the definition of isEq lives in :sweat_smile:

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:17):

so if it wasn't already in scope, the compiler would have to give an error

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:17):

the current plan is for abilities not to have type parameters

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:17):

so you can define an ability Equating but not IsDict k v

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:18):

because we'd need either higher-kinded types or associated types to allow something like that

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:19):

e.g. Dict.insert : Dict k v, k, v -> Dict k v works fine if Dict is an opaque type

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:20):

but if we want to make it an ability, then it would need to be something like Dict.insert : dict, k v -> d | dict supports IsDict k v

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:21):

and then map gets kinda weird, e.g. instead of Dict.map : Dict k v1, (k, v1 -> v2) -> Dict k v2 we have Dict.map : d1, (k, v1 -> v2) -> d2 | d1 supports IsDict k v1 | d2 supports IsDict k v2

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:24):

but more to the point, at that point you can do Monad.bind : m1, (a -> m2) -> m2 | m1 supports Monad a | m2 supports Monad b

view this post on Zulip Richard Feldman (Mar 21 2022 at 16:25):

and there's a whole FAQ entry about why not to support that :big_smile:

view this post on Zulip Brendan Hansknecht (Mar 21 2022 at 16:45):

ah, didn't think about the higher kinded type part. yeah. makes sense.

view this post on Zulip Brendan Hansknecht (Mar 21 2022 at 17:28):

You could still do

dict =
  Dict.empty
    . insert k1 v1
    . insert k2 v2

... I guess that technically makes a unique syntax that we could parse. Still has the other naming and higher kindled type problems, but at least doesn't break parsing.


Last updated: Jul 26 2025 at 12:14 UTC