Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record property vs dot functions #177

Closed
chrisdone opened this issue Jun 19, 2021 · 4 comments
Closed

Record property vs dot functions #177

chrisdone opened this issue Jun 19, 2021 · 4 comments

Comments

@chrisdone
Copy link

I was wondering how you support record field access vs method calls in Koka.

If person.name is usually record access, and Koka has person.name.reverse for reverse(person.name), that seems to conflict. What if an object has a function called reverse as a field?

D chooses the object’s field first if it has one.

@daanx
Copy link
Member

daanx commented Jun 19, 2021

Ah, the interesting thing for Koka is that there are no field selectors as such; for a struct like:

struct person {  age : int }

Koka generates a primitive function fun age( p : person ) : int and the dot is only syntactic sugar for functions. So person.name.reverse always equals reverse( name( person ) ) -- functions all the way ! :-)

Moreover, there is no "field assignment" either, Koka just generates a primitive copy function for data types with parameters the each have a default value set to the current record field:

fun copy( p : person, age : int = p.age ) : person

Now, we can write person.copy( age = 20 ) for example; For this, Koka uses the copy function by default when trying to apply a record, so this can be shortened to person( age = 20 ) as a "record update syntax" (but again, it is just functions again). (see the book for more info)

ps. The good part of this design is that it keeps the minimal but general principle: everything is just functions! the drawback is that it "pollutes" the namespace with field selector functions but since Koka has static overloading this works out reasonably well in practice -- wish to find a better solution to this though.

@chrisdone
Copy link
Author

Thanks! I see, so record accessors are just functions like in Haskell. So x.reverse could either mean “call the function from the struct” or “I wrote a function elsewhere, call that” and there is an overloading mechanism for functions.

How does the overloading work? 🤔

D uses the ad-hoc method (pick the struct field over a global function—leads to incoherence), Idris uses TDNR, which has bad inference. Rust picks one like D, but you can say Trait::method(obj) to be explicit.

I also have adopted x.foo() as syntactic sugar for foo(x) in my own pure FP language, but had to solve this namespacing issue before I could buy into it. I have my own solution, but am interested in others.

The copying example is interesting! I was debating whether to do simple updates or bake lenses in. Undecided as yet.

@tom-sherman
Copy link

tom-sherman commented Jul 28, 2022

Also wondering about this. Is there a solution for the ambiguous identifier problem?

Right now I'm getting around this by manually making the identifiers non-ambiguous by eg. prepending the module name (see #290). This is extra boilerplate I'd rather not do.

@TimWhiting
Copy link
Collaborator

@chrisdone
With the latest release of Koka, all identifiers must be locally qualified if ambiguous, and property accessor functions are locally qualified by the type name. This way, all identifiers can be referred to unambiguously by providing qualified identifiers.

Overloading now works by looking up the name in a map of names to fully qualified names. If it is ambiguous it tries to find an unambiguous one based on the type. See the latest samples/syntax/qualifiers.

In response to the original question:
person.name.reverse for reverse(person.name)

Type inference tries to find the function first, but if it is ambiguous then it tries to infer the types of the arguments to narrow the options. So in this case, it will first see if there is a single function called reverse. If there is multiple then it will infer the subexpression person.name first and use it's type to try to disambiguate. This works very well most of the time. If you run into issues with the latest release, open a new issue or a new discussion topic, we definitely want feedback on the changes to the overloading in Koka!

@tom-sherman I believe the error messages and disambiguation should work better now, but please give the new release a try and create new discussions / issues for feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants