-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
WIP: redesign closures, then generic functions #13412
Conversation
What's Edit: sorry, press |
That looks very interesting - since in the first example, using map and the x->2x function was actually faster than scalar * array, would you want to in general rewrite scalar * array & array * scalar to use map? |
Currently just
I think that's mostly due to when GC happened to run. However (1) |
FA is going to duck, dive, and dodge just to make it more sporting. Then happily roll over and join the party. |
I'd like to get a head start on some syntax bikeshedding here. The
will become
Of course, if you want to refer to the function and its fields in the body, you can also give it a name:
This means that plain old Things get trickier when you add static parameters. Obviously we need to take the opportunity to solve #11310 at the same time. A simple translation of
but that's a bit strange. Using my proposal #10146 (comment), we get
which is nicer, but strange in that the second
since then it's clear that the static parameters wrap the whole signature. However this doesn't seem to be usable with short-form definitions, which is unfortunate. |
cc @one-more-minute @IainNZ @yuyichao @StefanKarpinski |
What about something like:
I dunno how this would be enforced. Regular function calls would be unchanged.
|
Double curly parameters would never cease to confuse me. I'd always have to have the doc page that explains which does what open any time I read or write code that needs them. Maybe losing out on the short form convenience for parameterized call methods wouldn't be a huge loss and worth using syntax with more of a distinction. |
Slightly different syntax to set it off? your mention of "double curly" made me wonder if function {{T,S}} Foo{T}(x::Bar{S})
...
end |
Switching to the "just try it and see what happens" approach, it turns out that
and
parse just fine (to an implicit multiplication of |
|
Maybe, but it's recognized as two expressions, i.e.
works. |
Do we need a single-line syntax for this? Couldn't the |
I agree it's acceptable, just slightly half-assed. |
Perhaps but this is turning into a bit of a syntax trainwreck. Don't get me wrong, I love the new design but I don't care for any of the syntax options proposed here. I would be happy if we could deobfuscate the dreaded parametric inner constructor business while we're at it, so I think that should be considered too. |
As I already said, we should definitely fix #11310 at the same time. All of the options here would in fact fix the constructor syntax issue. Calling it a trainwreck isn't very productive. This is how the sausage is made. |
I'm just following along at the periphery, but I have a suggestion that makes sense to me as a user and won't have people scrambling to find the manual every time they encounter two sets of curly braces. The two sets of curly braces serve two different purposes. One is the tuple of type parameters belonging to the type. The other declares the type parameters. I was under the impression that Instead, I think the following syntax is very intuitive and doesn't confuse the meaning of
This short form syntax is analogous to the comprehension syntax where variables are declared after they are used ( |
That's not a bad idea at all. Though a bit more verbose, I definitely see the appeal over a salad of curly braces. |
Also, as some might already know, when this form of polymorphism was first invented the feature was called "where clauses" (http://www.pmg.lcs.mit.edu/papers/where-clauses.pdf). |
I do like that suggestion, but I'd tweak the short-form to be Foo{T}(bar::S) where T <: AbstractBar, S = ... "explaining the type parameters" feels like it should be sooner after their usage. |
What about something like: function Foo{T,S where T <: AbstractBar}(bar::S)
...
end
Foo{T,S where T <: AbstractBar}(bar::S) = ... This has a few advantages: I think it could be compatible with the current form |
Sorry if I am misunderstanding the problem but would it be possible to use a separator in the parameter declaration (curly bit after the function/constructor name) to differentiate function-like types and argument-like types. Everything before the separator would refer to the type of the function and everything after would refer to the types of the function's arguments. Something like function Foo{T <: AbstractBar; S}(bar::S)
...
end
Foo{T <: AbstractBar; S)(bar::S) = ... with |
@lstagner I like your suggestion as well, it has the same advantages as my suggestion, and furthermore it eliminates having T twice, in declaraction & constraint. |
@@ -1,6 +1,6 @@ | |||
LLVM_VER = 3.3 | |||
LLVM_LIB_SUFFIX = | |||
PCRE_VER = 10.20 | |||
PCRE_VER = 10.10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry -- just accidentally pushed something I need in my local build (#12038 (comment)). Will be edited out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, that bizarre one. Carry on, just making sure this gets noted.
Just as Any has ANY, add FUNCTION for Function. You might want to use e.g. |
This is a great improvement in many ways (anonymous functions are fast!), but I'm hoping that c-callable closures is on the horizon; passing closures to C libraries for optimization etc. are a natural use-case (see jump-dev/NLopt.jl#7). Is there an issue open somewhere tracking progress on this? Is #13984 a way forward? |
I'm not sure if this constitutes expected behavior (it wasn't clear to me how things ended up based on the discussion above), but it definitely appears to be a rather subtle change from 0.4 to 0.5 and I think it is related to this change. The snippet below runs fine, and produces a vector of dictionaries on 0.4.3, but results in a stack overflow on 0.5: typealias A Vector{Dict{Int, Float64}}
A(n::Int) = [Dict{Int, Float64}() for _ = 1:n]
a = A(3)
println(a) There might be a simpler example, but this is what I came up with because it is similar to some code I actually have. A similar snippet works as expected (by me) on 0.4.3 but results in a segfault on 0.5: typealias A Vector{Float64}
A(n::Int) = [Float64(1.0) for _ = 1:n]
a = A(3)
println(a) It appears that the code defines a function called |
It actually does the same thing in both versions, which is add a method to the type. This overrides constructors for the |
Although the following is a very contrived toy example, it works with v0.4 but not with v0.5; I am still trying to get used to the new syntax for closures - how can I recode the following so that it works on v0.5?
|
Stealing from Concurrent Clean, recognizing that a function is typed by both its domain and range we could differentiate the parameters for the input from the output by over using the mapping symbol "->" in the parameter list: myfoofunction{P,Q,R,...->S,TU,...}(x::P, y::Q, z::R,...)::myfootype{S,T,U,...} = do stuff... In rough syntax: function name{input type parameters -> output type parameters}(argument list::argument types{input parameters})::outputtype{output parameters} = do stuff... Of course we would also want to allow linking the parameters: myotherfunction{T->T}(x::T)::T = do stuff... But it seems it might be a bit late in the game to propose this syntax, and I don't know if that will catch all the use cases properly. Also, could it be possible to parameterize the type "Function", so that it is Function{InType,OutType}? Yielding: Function{In,Out}<:Function{In}<:Function We could then have foo(x::Int, y::Int)::Float = convert(Float, x+y) isa(f, Function{Tuple{Int,Int},Float}) == true and for closures, with all the options: g{T->S,U}(y::T)::Function{S,U} = (x::S -> y...)::U reading in long form: "g" is a function with input types parameterized by "T" and output types parameterized "S" and "U", that returns a function with input type parameterized by the "S" from "g" and output type parameterized by the "U" from "g". Where the return of "g" equals the mapping "x" of type "S" from "g" to an output of type "U" from "g". Finally in the long run I would like to see function argument patterns, particularly with constants, handled by a mechanism a bit more transparent than the current Val{}() ::Type{Val{}} pairing. Currently a syntax like: f(x::{Int64})=x+1 Throws a type error. So again is there a possibility of introducing the syntax f(x::{0}) = 1 For detecting a specific constant? The meaning of "x::{a}" is "x" is exactly literal "a"; with anything other than a literal being forbidden. Of course this leads to interesting conundrums like: f(x::{eval(:(some expression))}) = ... This would allow f(x::{0}) = ... to become a fixed look up behind the scenes |
…d in julia v0.5 See: JuliaLang/julia#13412 Can possibly improve this by storing V as an Expression instead of a String NB: To create the sparse matrix, V must be a single expression containing an array, hence :([expr; expr; …]). Otherwise, the previous way, storing an array of expressions: [:(expr); :(expr); …] required that the variables endo, exo, and param be defined in the space where the function was going to be called, which was less than ideal. If I can figure out a way to pass the value to the function, then this would be ideal. NB: Also that the solution outlined here: https://groups.google.com/forum/#!topic/julia-users/87_BMRY7Du4 does not work when ex is an array of expressions: [:(expr); :(expr); …]
…d in julia v0.5 See: JuliaLang/julia#13412 Can possibly improve this by storing V as an Expression instead of a String NB: To create the sparse matrix, V must be a single expression containing an array, hence :([expr; expr; …]). Otherwise, the previous way, storing an array of expressions: [:(expr); :(expr); …] required that the variables endo, exo, and param be defined in the space where the function was going to be called, which was less than ideal. If I can figure out a way to pass the value to the function, then this would be ideal. NB: Also that the solution outlined here: https://groups.google.com/forum/#!topic/julia-users/87_BMRY7Du4 does not work when ex is an array of expressions: [:(expr); :(expr); …]
…d in julia v0.5 See: JuliaLang/julia#13412 Can possibly improve this by storing V as an Expression instead of a String NB: To create the sparse matrix, V must be a single expression containing an array, hence :([expr; expr; …]). Otherwise, the previous way, storing an array of expressions: [:(expr); :(expr); …] required that the variables endo, exo, and param be defined in the space where the function was going to be called, which was less than ideal. If I can figure out a way to pass the value to the function, then this would be ideal. NB: Also that the solution outlined here: https://groups.google.com/forum/#!topic/julia-users/87_BMRY7Du4 does not work when ex is an array of expressions: [:(expr); :(expr); …]
This is a very early preview of the likely future direction for how functions work. It's very much pre-alpha quality but does have a working REPL.This is now essentially done! I have one cool demo:Ok, maybe two cool demos:
FastAnonymous.jl, you are in my sights and your days are numbered.
My full plan has three stages, the first of which is now almost complete:
call
.call
function, and instead put method tables inside TypeName. Remove theFunction
concrete type. All generic functions use the same basic design as closures in stage 1.Some next steps:
Box
esjl_instantiate_staged
is probably a bit buggySome problems to solve:
Callable
type is now more urgent. I'd propose makingFunction
an abstract type, and making anonymous functions subtypes of it by default. That will at least handle all the cases in Base where function arguments had::Function
, and will also handle "functors" that had nothing to subtype from before.f
above,#2#g0
. There should be a nice standard name mangling, e.g.__hidden__.inner_1
.--compile=all
mode. This currently depends on the old implementation of closures.