Comments (7)
I'm quite reluctant to remove differing arity definitions for the same named function since it's common enough in the Erlang world that removing it might be a stumbling block for those coming from there but autocurrying makes enough sense to make it happen. My primary concern is bounding ambiguity - your proposal to simply drop out with a type error when we don't have enough information to make a decision seems pretty reasonable to me provided we make things clear enough for a user.
So the following would generate an error along the lines of {error, {ambiguous_application, foo/1, foo/2}}
- but maybe less hostile than that :)
foo x = x + x
foo x y = x + y
make_an_error () = 1 |> foo
While the following would not:
foo x = x + x
foo x y z = x + y + z
-- unit -> (int -> int)
passes_typing () = 2 |> foo 1
Thoughts? Am I clinging too tightly to the differing arity thing?
from alpaca.
I think the first arity error makes sense. What if there was also a curried/2
and curried/3
? In both Erlang and MLFE functions with the same name but different arity are considered different functions. The call curried 1 2 3
is thus ambiguous.
I don't know why the parenthesized version does not work. Spelling it out works:
test_curried () =
let f1 = (curried 1) in
let f2 = f1 2 in
f2 3
The exact error ((curried 1) 2) 3
fails with is:
{error,
{invalid_fun_application,
{error,
{invalid_fun_application,
{mlfe_apply,undefined,{symbol,13,"curried"},[{int,13,1}]},
[{int,13,2}]}},
[{int,13,3}]}}],
from alpaca.
Indeed, I'd mistakenly thought that only one function (regardless of arity) was allowed per name per module, therefore auto-currying would be possible, but I see that this was an oversight that is being corrected.
I agree with @danabr that multiple arity functions and auto-currying do not mix well. Scala has a special syntax for curried functions: http://docs.scala-lang.org/tutorials/tour/currying.html but I think that would be unnecessarily complicated. I guess my concern is that people familiar with other ML languages may be surprised by the lack of automatic currying, and things like point-free style are difficult to implement without it.
I suppose the typer could allow automatic currying if one and precisely one variant only of a function were defined, and throw an appropriate error in the case that currying would lead to ambiguous use. I guess the sweet spot is how to be a nice Erlang citizen while being as nice an ML as possible :)
The problem with not being able to apply the result of functions stems from here:
https://github.com/alpaca-lang/alpaca/blob/master/src/alpaca_parser.yrl#L404
As far as I understand it there's no rule to match applying on a general expression (specifically here, an apply expression). I wrote a test for the case and managed to write a rule in the parser, but I think that the alpaca_apply
record will need an extra field that can take a general expression as the function instead of just Name
as it has it currently. (theoretically this would also allow anonymous functions in future).
from alpaca.
I'm completely open to a discussion about multiple arity versions vs automatic currying, I just stuck that "fix" in today because it felt a little closer to Erlang and I was writing some Alpaca that could take advantage of it :) If it makes sense to back that out in favour of auto-currying I don't really have a problem with it tbh. Would be curious to hear arguments for/against.
As for the AST changes I think we're going to end up wrapping the returns in core-erlang let-bindings in the code generation stage as the following:
-module(test).
curried(X) ->
fun(Y) -> X + Y end.
apply_curried() ->
(curried(2))(3).
becomes this in core Erlang (some output redacted for brevity):
'curried'/1 =
%% Line 3
fun (_cor0) ->
%% Line 4
( fun (_cor1) ->
call 'erlang':'+'
(_cor0, _cor1)
-| [{'id',{0,0,'-curried/1-fun-0-'}}] )
'apply_curried'/0 =
%% Line 6
fun () ->
let <_cor0> =
%% Line 7
apply 'curried'/1
(2)
in %% Line 7
apply _cor0
(3)
In this case should we have a different AST node for application of expressions or replace name
with the expectation of a name or an expression? Maybe the latter is best, it's going to result in a branch in the code generation step regardless.
from alpaca.
I like the latter approach - the AST node would have a 'function' part which is either a name of a function or an expression which yields a function.
I'll present my argument here for my preferences on autocurrying.
I'm very much in favour of autocurrying. SML, Miranda, Haskell, Elm and OCaml all implement it, and it makes the language very malleable. I propose we allow autocurrying and multiple arity functions, but if more than one variant of a function is defined and not enough arguments are provided to match any variant, the typer errors with an error message explaining that autocurrying is disallowed in these circumstances (and suggest renaming the variants).
If push came to shove, I'd personally prefer autocurrying to multiple arity. I don't think the loss is too great - you just have to give functions different names - but you gain a great deal. One example is that a function like |>
otherwise only works on functions which take one argument but with autocurrying it works with more, as in:
-- Imaginary function filter == (a -> bool) -> list a -> list a
let filtered =
[1, 2, 2, 3, 3, 3] |> filter ((==) 2)
-- result: filtered == [2, 2]
...so this imaginary filter function takes a predicate function that returns a bool, a list of 'a and returns a list of 'a. We call filter with only one argument, so we get a function that is 'waiting' for a list. |>
applies the value on the left to the function on the right, which is now the curried form that only takes one argument, and everything works out :) I snook another example in there too - ((==) 2)
being a prefix invocation of ==
, meaning we get a curried function that takes a single argument that will return true if the argument is 2. The level of expressivity you can achieve with a really small toolbox is incredible, and people coming from Elm, OCaml etc., will feel right at home with it.
Counter-arguments for me are - we're in Erlang land and multiple arity is a natural fit there. Also we can emulate autocurrying above by providing a standard library with 'pre-curried' functions with different arities and functions for currying, and at worst we can always return / wrap using anonymous functions, once those are implemented. It's also possible to get carried away with techniques like point free style which can lead to terse and hard to read code.
from alpaca.
I think that looks great. I'm sure if it ever does get confusing there will be ways of disambiguating, syntactically if needs be.
from alpaca.
I've opened issue #74 to address the automatic currying piece of this so we can fix the basic expression application problem on this one.
from alpaca.
Related Issues (20)
- Function parsing as incorrect arity with ADT as first arg HOT 2
- Auto-imported pipe operator HOT 2
- Treat types with a single unqualified member as aliases HOT 1
- Overlap between function heads and match expression HOT 1
- How to type Erlang terms without built-in types. HOT 7
- alias module name HOT 1
- Zero arity functions HOT 5
- Inconsistent function case HOT 2
- Compile error with type signature annotation
- Defining the function (>>) does not yield a helpful error
- Use values to signify test pass/fail rather than exceptions
- Can't find a way to contact the creator/mantainers of alpaca :) HOT 2
- ReasonML HOT 4
- provide prebuilt "binaries" for OTP 21
- Status? HOT 4
- "Overloaded" records with constructors in patterns fail instead of getting a proper warning.
- Incorrect line numbers for missing record field errors
- clarify - nominal or structural type system HOT 3
- ADT argument in function header crashes compiler
- Non-exhaustive patterns in "match" expressions HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from alpaca.