<p>In my WIP language, all functions are really just variables with a callable type. That is, a function call <code>foo(bar)</code> is parsed into the following AST:</p>
<pre><code>         CallOperation
           /       \
         expr     args
         /           \
 VariableAccess  VariableAccess
        |               |
       foo             bar
</code></pre>
<p>All functions defined on structs are similarly stored as callable fields. A method call <code>foo.bar()</code> is really just the variable access <code>foo.bar</code>, then a call on the resulting callable.</p>
<p>However, my language also has a typeclass system, like Rust. Unlike structs, traits have to “own” their functions, that is, they cannot simply be fields.</p>
<p>When performing the type checking, as struct members are conceptually fields on the struct, I need to do no extra work. Just access the field and then call it. As far as the type checker is concerned, when it sees a method call, it’s looking at a field access. However, trait methods are not defined as fields. The type checker can’t see that the access is actually a trait method call. How can I resolve this condundrum?</p>

<implementation><static-typing><type-checker><traits>
How can I reconcile “all functions are variables” with a typeclass type system?
<p><strong>Different syntax</strong></p>
<p>It wouldn't be unreasonable to require a different syntax for accessing fields and calling trait methods. One problem this are that a lot of languages use <code>.</code> for both, so that's what people are used to, and it's hard to come up with a different syntax that isn't weird-looking or verbose.</p>
<p>Requiring something like <code>foo::bar</code> for field accesses may be acceptable to you if you think your users won't be using field accesses too often (if your language has great pattern matching, for example).</p>

CC BY-SA 4.0
2023-05-19T16:56:30.387Z
2023-05-19T16:56:30.387Z
user
<p><strong>UFCS</strong></p>
<p>You can use something like UFCS. Special-case expression of the form <code>foo.bar(...args)</code>. If your compiler encounters an expression of the form <code>foo.bar(baz)</code>, but <code>foo</code> doesn't have a field called <code>bar</code>, make it look for a function <code>bar</code> in the current scope and treat the call as equivalent to <code>bar(foo, baz)</code>.</p>

CC BY-SA 4.0
2023-05-19T16:42:19.930Z
2023-05-19T16:56:49.437Z
2023-05-19T16:56:49.437Z
user
CC BY-SA 4.0
2023-05-19T16:43:26.783Z
Thing is, as explained in the question, the type checker doesn't know whether it's a function call or just a field access
Seggan
CC BY-SA 4.0
2023-05-19T16:45:27.403Z
You'll just have to special-case expressions of that form (if you decide to use this approach). In my own language (unimplemented D:), I have a tree type called `FnCall` or something like that created in the parsing phase itself, and only later is it determined whether `foo.bar(baz)` refers to a field `bar` or a free function `bar`
user
CC BY-SA 4.0
2023-05-20T02:48:56.207Z
There is no need to split your answer across multiple answers ─ when a question asks for a list of options, or pros/cons, it's fine (and good) for an answer to discuss more than one option or consideration.
kaya3
CC BY-SA 4.0
2023-05-20T15:59:41.083Z
@kaya3 I thought it was decided on meta that we didn't want multiple answers combined into one (granted, I only suggested two options, not a whole list of them, but I figured I'd go with the consensus)
user
CC BY-SA 4.0
2023-05-20T16:02:14.857Z
After writing my comment I realised that there were reasons the community might prefer separate answers ─ but I didn't find the specific case of multiple answers by the same author discussed on meta. So I started a discussion about that [here](https://languagedesign.meta.stackexchange.com/q/171/89) ─ it seems there is no consensus yet but you may want to add your view there.
kaya3
<p>In the course of writing this question, I found an answer. Feel free to add your own answers however.</p>
<p>This can be solved by simply making the trait implementation &quot;add&quot; its methods to the struct's fields. Therefore, as far as the type checker is concerned, the trait's methods are just another fields on the struct. This has another bonus as <em>not</em> calling the method (but just accessing it) returns the callable which can be used or stored for later.</p>

CC BY-SA 4.0
2023-05-19T16:39:45.567Z
2023-05-19T16:39:45.567Z
Seggan
CC BY-SA 4.0
2023-06-20T16:34:24.577Z
Additional note: if different traits can have colliding method names, like in Rust, then this can still work, but the trait methods must only be added to the type's fields while the trait is in scope.
kaya3
# How can I reconcile “all functions are variables” with a typeclass type system?
Posted by: username_0 on 2023-05-19
In my WIP language, all functions are really just variables with a callable type. That is, a function call foo(bar) is parsed into the following AST:
         CallOperation
           /       \
         expr     args
         /           \
 VariableAccess  VariableAccess
        |               |
       foo             bar

All functions defined on structs are similarly stored as callable fields. A method call foo.bar() is really just the variable access foo.bar, then a call on the resulting callable.
However, my language also has a typeclass system, like Rust. Unlike structs, traits have to “own” their functions, that is, they cannot simply be fields.
When performing the type checking, as struct members are conceptually fields on the struct, I need to do no extra work. Just access the field and then call it. As far as the type checker is concerned, when it sees a method call, it’s looking at a field access. However, trait methods are not defined as fields. The type checker can’t see that the access is actually a trait method call. How can I resolve this condundrum?
**Tags:** implementation static-typing type-checker traits
---
## 3 answers
### Answer by username_1 on 2023-05-19. Score: 5
**Different syntax**

It wouldn't be unreasonable to require a different syntax for accessing fields and calling trait methods. One problem this are that a lot of languages use `.` for both, so that's what people are used to, and it's hard to come up with a different syntax that isn't weird-looking or verbose.

Requiring something like `foo::bar` for field accesses may be acceptable to you if you think your users won't be using field accesses too often (if your language has great pattern matching, for example).
---
### Answer by username_1 on 2023-05-19. Score: 4
**UFCS**

You can use something like UFCS. Special-case expression of the form `foo.bar(...args)`. If your compiler encounters an expression of the form `foo.bar(baz)`, but `foo` doesn't have a field called `bar`, make it look for a function `bar` in the current scope and treat the call as equivalent to `bar(foo, baz)`.
> **Comments:**
> **username_0** (2023-05-19): Thing is, as explained in the question, the type checker doesn't know whether it's a function call or just a field access  
> **username_1** (2023-05-19): You'll just have to special-case expressions of that form (if you decide to use this approach). In my own language (unimplemented D:), I have a tree type called `FnCall` or something like that created in the parsing phase itself, and only later is it determined whether `foo.bar(baz)` refers to a field `bar` or a free function `bar`  
> **username_2** (2023-05-20): There is no need to split your answer across multiple answers ─ when a question asks for a list of options, or pros/cons, it's fine (and good) for an answer to discuss more than one option or consideration.  
> **username_1** (2023-05-20): @kaya3 I thought it was decided on meta that we didn't want multiple answers combined into one (granted, I only suggested two options, not a whole list of them, but I figured I'd go with the consensus)  
> **username_2** (2023-05-20): After writing my comment I realised that there were reasons the community might prefer separate answers ─ but I didn't find the specific case of multiple answers by the same author discussed on meta. So I started a discussion about that [here](https://languagedesign.meta.stackexchange.com/q/171/89) ─ it seems there is no consensus yet but you may want to add your view there.  
---
### Answer by username_0 on 2023-05-19. Score: 3
In the course of writing this question, I found an answer. Feel free to add your own answers however.

This can be solved by simply making the trait implementation "add" its methods to the struct's fields. Therefore, as far as the type checker is concerned, the trait's methods are just another fields on the struct. This has another bonus as *not* calling the method (but just accessing it) returns the callable which can be used or stored for later.
> **Comments:**
> **username_2** (2023-06-20): Additional note: if different traits can have colliding method names, like in Rust, then this can still work, but the trait methods must only be added to the type's fields while the trait is in scope.  
---
