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

Function chaining #5571

Open
shelakel opened this issue Jan 27, 2014 · 224 comments

Comments

@shelakel
Copy link

commented Jan 27, 2014

Would it be possible to allow calling any function on Any so that the value is passed to the function as the first parameter and the parameters passed to the function call on the value is added afterwards?
ex.

sum(a::Int, b::Int) -> a + b

a = 1
sum(1, 2) # = 3
a.sum(2) # = 3 or
1.sum(2) # = 3

Is it possible to indicate in a deterministic way what a function will return in order to avoid run time exceptions?

@JeffBezanson

This comment has been minimized.

Copy link
Member

commented Jan 27, 2014

The . syntax is very useful, so we aren't going to make it just a synonym for function call. I don't understand the advantage of 1.sum(2) over sum(1,2). To me it seems to confuse things.

Is the question about exceptions a separate issue? i think the answer is no, aside from wrapping a function body in try..catch.

@shelakel

This comment has been minimized.

Copy link
Author

commented Jan 27, 2014

The 1.sum(2) example is trivial (I also prefer sum(1,2)) but it's just to demonstrate that a function isn't owned per se by that type ex. 1 can be passed to a function with the first parameter being a Real, not just to functions that expect the first parameter to be an Int.

Edit: I might have misunderstood your comment. Dot functions will be useful when applying certain design patterns such as the builder pattern commonly used for configuration. ex.

validate_for(name).required().gt(3) 
# vs 
gt(required(validate_for(name)), 3) 

The exceptions I was just referring to is due to functions returning non-deterministic results (which is anyway bad practice). An example would be calling a.sum(2).sum(4) where .sum(2) sometimes return a String instead of an Int but .sum(4) expects an Int. I take it the compiler/runtime is already smart enough to evaluate such circumstances - which would be same when nesting the function sum(sum(1, 2), 4) - but the feature request would require extending said functionality to enforce type constraints on dot functions.

@ssfrr

This comment has been minimized.

Copy link
Contributor

commented Jan 27, 2014

One of the use cases people seem to like is the "fluent interface". It's sometimes nice in OOP APIs when methods return the object, so you can do things like some_obj.move(4, 5).scale(10).display()

For me I think that this is better expressed as function composition, but the |> doesn't work with arguments unless you use anon. functions, e.g. some_obj |> x -> move(x, 4, 5) |> x -> scale(x, 10) |> display, which is pretty ugly.

One option to support this sort of thing would be if |> shoved the LHS as the first argument to the RHS before evaluating, but then it couldn't be implemented as a simple function as it is now.

Another option would be some sort of @composed macro that would add this sort of behavior to the following expression

You could also shift responsibility for supporting this to library designers, where they could define

function move(obj, x, y)
    # move the object
end

move(x, y) = obj -> move(obj, x, y)

so when you don't supply an object it does partial function application (by returning a function of 1 argument) which you could then use inside a normal |> chain.

@kmsquire

This comment has been minimized.

Copy link
Member

commented Jan 27, 2014

Actually, the definition of |> could probably be changed right now to the
behavior your asking for. I'd be for it.

On Monday, January 27, 2014, Spencer Russell notifications@github.com
wrote:

One of the use cases people seem to like is the "fluent interface". It's
sometimes nice in OOP APIs when methods return the object, so you can do
things like some_obj.move(4, 5).scale(10).display()

For me I think that this is better expressed as function composition, but
the |> doesn't work with arguments unless you use anon. functions, e.g. some_obj
|> x -> move(x, 4, 5) |> x -> scale(x, 10) |> display, which is pretty
ugly.

One option to support this sort of thing would be if |> shoved the LHS as
the first argument to the RHS before evaluating, but then it couldn't be
implemented as a simple function as it is now.

Another option would be some sort of @Composed macro that would add this
sort of behavior to the following expression

You could also shift responsibility for supporting this to library
designers, where they could define

function move(obj, x, y)
# move the object
end

move(x, y) = obj -> move(obj, x, y)

so when you don't supply an object it does partial function application
(by returning a function of 1 argument) which you could then use inside a
normal |> chain.


Reply to this email directly or view it on GitHubhttps://github.com//issues/5571#issuecomment-33408448
.

@shelakel

This comment has been minimized.

Copy link
Author

commented Jan 27, 2014

ssfrr I like the way you think! I was unaware of the function composition |>. I see there's recently been a similar discussion [https://github.com//issues/4963].

kmsquire I like the idea of extending the current function composition to allow you to specify parameters on the calling function ex. some_obj |> move(4, 5) |> scale(10) |> display. Native support would mean one less closure, but what ssfrr suggested is a viable way for now and as an added benefit it should also be forward compatible with the extended function composition functionality if it gets implemented.

Thanks for the prompt responses :)

@kmsquire

This comment has been minimized.

Copy link
Member

commented Jan 27, 2014

Actually, @ssfrr was correct--it isn't possible to implement this as a simple function.

@jakebolewski

This comment has been minimized.

Copy link
Member

commented Jan 27, 2014

What you want are threading macros (ex. http://clojuredocs.org/clojure_core/clojure.core/-%3E). Unfortunate that @-> @->> @-?>> is not viable syntax in Julia.

@ssfrr

This comment has been minimized.

Copy link
Contributor

commented Jan 27, 2014

Yeah, I was thinking that infix macros would be a way to implement this. I'm not familiar enough with macros to know what the limitations are.

@kmsquire

This comment has been minimized.

Copy link
Member

commented Jan 27, 2014

I think this works for @ssfrr's compose macro:

Edit: This might be a little clearer:

import Base.Meta.isexpr
_ispossiblefn(x) = isa(x, Symbol) || isexpr(x, :call)

function _compose(x)
    if !isa(x, Expr)
        x
    elseif isexpr(x, :call) &&    #
        x.args[1] == :(|>) &&     # check for `expr |> fn`
        length(x.args) == 3 &&    # ==> (|>)(expr, fn)
        _ispossiblefn(x.args[3])  #

        f = _compose(x.args[3])
        arg = _compose(x.args[2])
        if isa(f, Symbol)
            Expr(:call, f, arg) 
        else
            insert!(f.args, 2, arg)
            f
        end
    else
        Expr(x.head, [_compose(y) for y in x.args]...)
    end
end

macro compose(x)
    _compose(x)
end
julia> macroexpand(:(@compose x |> f |> g(1) |> h('a',"B",d |> c(fred |> names))))
:(h(g(f(x),1),'a',"B",c(d,names(fred))))
@StefanKarpinski

This comment has been minimized.

Copy link
Member

commented Jan 27, 2014

If we're going to have this |> syntax, I'd certainly be all for making it more useful than it is right now. Using just to allow putting the function to apply on the right instead of the left has always seemed like a colossal waste of syntax.

@malmaud

This comment has been minimized.

Copy link
Contributor

commented Jan 27, 2014

+1. It's especially important when you are using Julia for data analysis, where you commonly have data transformation pipelines. In particular, Pandas in Python is convenient to use because you can write things like df.groupby("something").aggregate(sum).std().reset_index(), which is a nightmare to write with the current |> syntax.

@cdsousa

This comment has been minimized.

Copy link
Contributor

commented Jan 28, 2014

👍 for this.

(I'd already thought in suggesting the use of the .. infix operator for this (obj..move(4,5)..scale(10)..display), but the operator |> will be nice too)

@malmaud

This comment has been minimized.

Copy link
Contributor

commented Jan 31, 2014

Another possibility is adding syntactic sugar for currying, like
f(a,~,b) translating to x->f(a,x,b). Then |> could keep its current meaning.

@ssfrr

This comment has been minimized.

Copy link
Contributor

commented Jan 31, 2014

Oooh, that would be a really nice way to turn any expression into a function.

Possibly something like Clojure's anonymous function literals, where #(% + 5) is shorthand for x -> x + 5. This also generalizes to multiple arguments with %1, %2, etc. so #(myfunc(2, %1, 5, %2) is shorthand for x, y -> myfunc(2, x, 5, y)

Aesthetically I don't think that syntax fits very well into otherwise very readable julia, but I like the general idea.

To use my example above (and switching to @malmaud's tilde instead of %), you could do

some_obj |> move(~, 4, 5) |> scale(~, 10) |> display

which looks pretty nice.

This is nice in that it doesn't give the first argument any special treatment. The downside is that used this way we're taking up a symbol.

Perhaps this is another place where you could use a macro, so the substitution only happens within the context of the macro.

@StefanKarpinski

This comment has been minimized.

Copy link
Member

commented Jan 31, 2014

We obviously can't do this with ~ since that's already a standard function in Julia. Scala does this with _, which we could also do, but there's a significant problem with figuring out what part of the expression is the anonymous function. For example:

map(f(_,a), v)

Which one does this mean?

map(f(x->x,a), v)
map(x->f(x,a), v)
x->map(f(x,a), v)

They're all valid interpretations. I seem to recall that Scala uses the type signatures of functions to determine this, which strikes me as unfortunate since it means that you can't really parse Scala without knowing the types of everything. We don't want to do that (and couldn't even if we wanted to), so there has to be a purely syntactic rule to determine which meaning is intended.

@ssfrr

This comment has been minimized.

Copy link
Contributor

commented Feb 2, 2014

Right, I see your point on the ambiguity of how far to go out. In Clojure the whole expression is wrapped in #(...) so it's unambiguous.

In Julia is it idiomatic to use _ as don't-care value? Like x, _ = somfunc() if somefunc returns two values and you only want the first one?

To solve that I think we'd need macro with an interpolation-like usage:

some_obj |> @$(move($, 4, 5)) |> @$(scale($, 10)) |> display

but again, I think it's getting pretty noisy at that point, and I don't think that @$(move($, 4, 5)) gives us anything over the existing syntax x -> move(x, 4, 5), which is IMO both prettier and more explicit.

I think this would be a good application of an infix macro. As with #4498, if whatever rule defines functions as infix applied to macros as well, we could have a @-> or @|> macro that would have the threading behavior.

@malmaud

This comment has been minimized.

Copy link
Contributor

commented Feb 2, 2014

Ya, I like the infix macro idea, although a new operator could just be introduced for this use in lieu of having a whole system for inplace macros. For example,
some_obj ||> move($,4,5) ||> scale($, 10) |> disp
or maybe just keep |> but have a rule that
x |> f implicitly transforms into x |> f($):
some_obj |> scale($,10) |> disp

@meglio

This comment has been minimized.

Copy link

commented Feb 6, 2014

Folks, it all really looks ugly: |> ||> etc.
So far I found out Julia's syntax to be so clear that these things discussed above doesn't look so pretty if compared to anything else.

In Scala it's probably the worst thing - they have so much operators like ::, :, <<, >> +:: and so on - it just makes any code ugly and not readable for one without a few months of experience in using the language.

@johnmyleswhite

This comment has been minimized.

Copy link
Member

commented Feb 6, 2014

Sorry to hear you don't like the proposals, Anton. It would be helpful if you made an alternative proposal.

@meglio

This comment has been minimized.

Copy link

commented Feb 6, 2014

Oh sorry, I am not trying to be unkind. And yes - critics without proposals
are useless.

Unfortunately I am not a scientist constructing languages so I just do not
know what to propose... well , except making methods optionally owned by
objects as it is in some languages.

@malmaud

This comment has been minimized.

Copy link
Contributor

commented Feb 6, 2014

I like the phrase "scientist constructing languages" - it sounds much more grandiose than numerical programmers sick of Matlab.

I feel that almost every language has a way to chain functions - either by repeated application of . in OO languages, or special syntax just for that purpose in more functional languages (Haskell, Scala, Mathematica, etc.). Those latter languages also have special syntax for anonymous function arguments, but I don't think Julia is really going to go there.

I'll reiterate support for Spencer's proposal - x |> f(a) get translated into f(x, a), very analogously to how do blocks works (and it reinforces a common theme that the first argument of a function is privileged in Julia for syntactic sugar purposes). x |> f is then seen as short-hand for x |> f(). It's simple, doesn't introduce any new operators, handles the vast majority of cases that we want function chaining for, is backwards-compatible, and fits with existing Julia design principles.

@JeffBezanson

This comment has been minimized.

Copy link
Member

commented Feb 6, 2014

I also think that is the best proposal here, main problem being that it seems to preclude defining |> for things like I/O redirection or other custom purposes.

@ssfrr

This comment has been minimized.

Copy link
Contributor

commented Feb 6, 2014

Just to note, . is not a special function chaining syntax, but it happens to work that way if the function on the left returns the object it just modified, which is something that the library developer has to do intentionally.

Analogously, in Julia a library developer can already support chaining with |> by defining their functions of N arguments to return a function of 1 argument when given N-1 arguments, as mentioned here

That would seem to cause problems if you want your function to support variable number of args, however, so having an operator that could perform the argument stuffing would be nice.

@JeffBezanson, it seems that this operator could be implemented if there was a way to do infix macros. Do you know if there's an ideological issue with that, or is just not implemented?

@kmsquire

This comment has been minimized.

Copy link
Member

commented Feb 6, 2014

Recently, ~ was special-cased so that it quoted its arguments and calls
the macro @~ by default. |> could be made to do the same thing.

Of course, in a few months, someone will ask for <| to do the same...

On Thursday, February 6, 2014, Spencer Russell notifications@github.com
wrote:

Just to note, . is not a special function chaining syntax, but it happens
to work that way if the function on the left returns the object it just
modified, which is something that the library developer has to do
intentionally.

Analogously, in Julia a library developer can already support chaining
with |> by defining their functions of N arguments to return a function
of 1 argument when given N-1 arguments, as mentioned herehttps://github.com//issues/5571#issuecomment-33408448

That would seem to cause problems if you want your function to support
variable number of args, however, so having an operator that could perform
the argument stuffing would be nice.

@JeffBezanson https://github.com/JeffBezanson, it seems that this
operator could be implemented if there was a way to do infix macros. Do you
know if there's an ideological issue with that, or is just not implemented?


Reply to this email directly or view it on GitHubhttps://github.com//issues/5571#issuecomment-34374347
.

@ssfrr

This comment has been minimized.

Copy link
Contributor

commented Feb 6, 2014

right, I definitely wouldn't want this to be a special case. Handling it in your API design is actually not that bad, and even the variable arguments limitation isn't too much of an issue if you have type annotations to disambiguate.

function move(obj::MyType, x, y, args...)
    # do stuff
    obj
end

move(args...) = obj::MyType -> move(obj, args...)

I think this behavior could be handled by a @composable macro that would handle the 2nd declaration.

The infix macro idea is attractive to me in the situation where it would be unified with declaring infix functions, which is discussed in #4498.

@meglio

This comment has been minimized.

Copy link

commented Feb 7, 2014

Why Julia creators are so much against allowing objects to contain their own methods? Where could I read more about that decision? Which thoughts and theory are behind that decision?

@ihnorton

This comment has been minimized.

Copy link
Member

commented Feb 7, 2014

@meglio a more useful place for general questions is the mailing list or the StackOverflow julia-lang tag. See Stefan's talk and the archives of the users and dev lists for previous discussions on this topic.

@porterjamesj

This comment has been minimized.

Copy link
Member

commented Feb 7, 2014

Just chiming in, to me the most intuitive thing is to have some placeholder be replaced by the
value of the previous expression in the sequence of things you're trying to compose, similar to clojure's as-> macro. So this:

@as _ begin
    3+3
    f(_,y)
    g(_) * h(_,z)
end

would be expanded to:

g(f(3+3,y)) * h(f(3+3,y),z)

You can think of the expression on the previous line "dropping down" to fill the underscore hole on the next line.

I started sketching a tiny something like this last quarter in a bout of finals week procrastination.

We could also support a oneliner version using |>:

@as _ 3+3 |> f(_,y) |> g(_) * h(_,z)
@kmsquire

This comment has been minimized.

Copy link
Member

commented Feb 7, 2014

@porterjamesj, I like that idea!

@JeffBezanson

This comment has been minimized.

Copy link
Member

commented Feb 7, 2014

I agree; that is pretty nice, and has an appealing generality.
On Feb 7, 2014 3:19 PM, "Kevin Squire" notifications@github.com wrote:

@porterjamesj https://github.com/porterjamesj, I like that idea!

Reply to this email directly or view it on GitHubhttps://github.com//issues/5571#issuecomment-34497703
.

@o314

This comment has been minimized.

Copy link

commented Dec 15, 2017

@saolof

#Basic example:
f(a,b,c,d) = #some definition
f...(a)(b,c,d) == f(a,b,c,d)
f...(a,b)(c,d) == f(a,b,c,d)
f...(a,b,c)(d) == f(a,b,c,d)
f...(a)...(b)(c,d) == f(a,b,c,d) # etc etc

Good intuition for using splat with function chaining but too complex in my sense.
You made multiple application at one point of the chain.
In function chaining, you make one application step by step all along the chain.

And @StefanKarpinski is right, you dont know when to stop to apply functions over them-selves and finally apply them to a more scalar item.

--(clipped)--

Sorry, that's what rather pointless and unreadable.
See my second msg below to get clearer explanation (i hope).

@MikeInnes

This comment has been minimized.

Copy link
Member

commented Dec 17, 2017

Given how functional Julia is already, I quite like @saolof's idea of a function-curry operator. I don't really understand where the semantic objections are coming from, as it seems like this has a very obvious interpretation.

You can even prototype it from the comfort of your own repl:

ctranspose(f) = (a...) -> (b...) -> f(a..., b...)

map'(+)(1:10)

map'(+)'(1:10, 11:20)(21:30)

(+)'(1,2,3)(4,5)

1:10 |> map'(x->x^2) |> filter'(iseven)

Has kind of a nice feel to it, I think.

Edit: Feels like this could also be the path to generalising this more. If we can write map∘(+, 1:10) then we can write map∘(_, 1:10) to place the curried argument first, and the curry opertor determines the scope of the lambda, solving the biggest problem for such general currying.

@StefanKarpinski

This comment has been minimized.

Copy link
Member

commented Dec 17, 2017

Eh, that's clever, @MikeInnes.

@tomasaschan

This comment has been minimized.

Copy link
Member

commented Dec 18, 2017

I love how Julia's extreme extensibility shows off here too. The unifying solution to a very wide range of requirements for function chaining turns out to be abusing ctranspose...

(clarification: I'm getting 1:10 |> map'(x->x^2) |> filter'(iseven) with this proposal, so I'm 💯% for it!)

@StefanKarpinski

This comment has been minimized.

Copy link
Member

commented Dec 18, 2017

To be clear, I don't think we should actually abuse the adjoint operator for this, but it's a good proof of concept that we can have a concise function currying notation.

@tomasaschan

This comment has been minimized.

Copy link
Member

commented Dec 18, 2017

Maybe we should introduce a new unicode operator? http://www.fileformat.info/info/unicode/char/1f35b/index.htm

(Sorry...)

@bramtayl

This comment has been minimized.

Copy link
Contributor

commented Dec 18, 2017

I feel like _'s are still a much more flexible way to make lambdas

@yurivish

This comment has been minimized.

Copy link
Contributor

commented Dec 18, 2017

@bramtayl I think the idea in MikeInnes's edit to his post is that the two can coexist — standaline underscores as in @stevengj's pull request would work, standalone currying as in Mike's idea above would work, and combining the two would also work, allowing you to use the currying operator to delimit the scope of _s inside it.

@bramtayl

This comment has been minimized.

Copy link
Contributor

commented Dec 18, 2017

ah got it

@bramtayl

This comment has been minimized.

Copy link
Contributor

commented Dec 18, 2017

That makes it not too different from LazyCall.jl

@bramtayl

This comment has been minimized.

Copy link
Contributor

commented Dec 18, 2017

Or the proposal here: #24990 (comment)

@tomasaschan

This comment has been minimized.

Copy link
Member

commented Dec 18, 2017

On a more serious note:

To be clear, I don't think we should actually abuse the adjoint operator for this

Probably a sound choice. However, I would like to voice my hopes that if such a solution is implemented, it is given an operator which is easy to type. The ability to do something like 1:10 |> map'(x->x^2) is significantly less useful if whatever character replaces ' requires me to look it up in a unicode table (or use an editor which supports LaTeX-expansions).

@o314

This comment has been minimized.

Copy link

commented Jun 10, 2018

Rather than abusing the adjoint operator, we could reuse the splat one.

  • in a (linear) piping context
  • inside, in a function call
    • do splat before rather than after

so

  • splat can induce a missing iterator arg

A kind of high order splat, (with anacrusis if there is some musician there).
Hoping it shoud not shake too much the language.

EXAMPLE

1:10
    |> map(...x->x^2)
    |> filter(...iseven)

EXAMPLE 2

genpie = (r, a=2pi, n=12) ->
  (0:n-1) |>
      map(...i -> a*i/n) |>
      map(...t -> [r*cos(t), r*sin(t)]) 

could stand for

elmap = f -> (s -> map(f,s))

genpie = (r, a=2pi, n=12) ->
  (0:n-1) |>
      elmap(i -> a*i/n) |>
      elmap(t -> [r*cos(t), r*sin(t)]) 
@ivanctong

This comment has been minimized.

Copy link

commented Jul 4, 2018

Not sure if this belongs here, since the discussion has evolved to more advanced/flexible chaining and syntax... but back to the opening post, function chaining with dot syntax seems possible right now, with a little extra setup. The syntax is just a consequence of having dot syntax for structs along with first-class functions/closures.

mutable struct T
    move
    scale
    display
    x
    y
end

function move(x,y)
    t.x=x
    t.y=y
    return t
end
function scale(c)
    t.x*=c
    t.y*=c
    return t
end
function display()
    @printf("(%f,%f)\n",t.x,t.y)
end

function newT(x,y)
    T(move,scale,display,x,y)
end


julia> t=newT(0,0)
T(move, scale, display, 0, 0)

julia> t.move(1,2).scale(3).display()
(3.000000,6.000000)

The syntax seems very similar to conventional OOP, with a quirk of "class methods" being mutable. Not sure what the performance implications are.

@jballanc

This comment has been minimized.

Copy link
Contributor

commented Jul 4, 2018

@ivanctong What you've described is something more akin to a fluent interface than function chaining.

That said, solving the issue of function chaining more generally would have the added benefit of also being usable for fluent interfaces. While it is certainly possible to make something like a fluent interface using struct members in Julia currently, it strikes me as very much going against the spirit and design aesthetic of Julia.

@jaynagpaul

This comment has been minimized.

Copy link

commented Jul 30, 2018

The way elixir does it where the pipe operator always passes in the left-hand side as the first argument and allows extra arguments afterward, has been pretty useful, I would love to see something like "elixir" |> String.ends_with?("ixir") as a first class citizen in Julia.

@maurocchi

This comment has been minimized.

Copy link

commented Aug 18, 2018

Other languages define it as Uniform Function Call Syntax.
This feature offers several advantages (see Wikipedia), it would be nice if Julia support it.

@javadba

This comment has been minimized.

Copy link

commented Oct 19, 2018

So is there a fluent interface to Julia at this point?

@StefanKarpinski

This comment has been minimized.

Copy link
Member

commented Oct 19, 2018

Please post questions to the Julia discourse discussion forum.

@c42f

This comment has been minimized.

Copy link
Contributor

commented Nov 16, 2018

In a fit of hacking (and questionable judgement!?) I've created another possible solution to the tightness of binding of function placeholders:

https://github.com/c42f/MagicUnderscores.jl

As noted over at #24990, this is based on the observation that one often wants certain slots of a given function to bind an _ placeholder expression tightly, and others loosely. MagicUnderscores makes this extensible for any user defined function (very much in the spirit of the broadcast machinery). Thus we can have such things as

julia> @_ [1,2,3,4] |> filter(_>2, _)
2-element Array{Int64,1}:
 3
 4

julia> @_ [1,2,3,4] |> filter(_>2, _) |> length
2

"just work". (With the @_ obviously going away if it's possible to make this a general solution.)

@richiejp

This comment has been minimized.

Copy link

commented Dec 11, 2018

Some variation @MikeInnes suggestion would seem adequate for my needs (usually long chains of filter, map, reduce, enumerate, zip etc. using do syntax).

c(f) = (a...) -> (b...) -> f(a..., b...)

1:10 |> c(map)() do x
    x^2
end |> c(filter)() do x
    x > 50
end

This works, although I can't get ' to work anymore. It is slightly shorter than:

1:10 |> x -> map(x) do x
    x^2
end |> x -> filter(x) do x
    x > 50
end

Also I guess one could just do

cmap = c(map)
cfilter = c(filter)
cetc = c(etc)
...

1:10 |> cmap() do x
    x^2
end |> cfilter() do x
    x > 50
end |> cetc() do ...
@MikeInnes

This comment has been minimized.

Copy link
Member

commented Dec 11, 2018

As of 1.0 you'll need to overload adjoint instead of ctranspose. You can also do:

julia> Base.getindex(f::Function, x...) = (y...) -> f(x..., y...)

julia> 1:10 |> map[x -> x^2] |> filter[x -> x>50]
3-element Array{Int64,1}:
  64
  81
 100

If we could overload apply_type then we could get map{x -> x^2} :)

@t0mpr1c3

This comment has been minimized.

Copy link

commented Dec 26, 2018

@MikeInnes I just stole that

@t0mpr1c3

This comment has been minimized.

Copy link

commented Dec 26, 2018

A late and slightly frivolous contribution -- how about piping data to any location in the argument list using a combination of left and right curry operators:

VERSION==v"0.6.2"
import Base: ctranspose, transpose  
ctranspose(f::Function) = (a...) -> ((b...) -> f(a..., b...))  
 transpose(f::Function) = (a...) -> ((b...) -> f(b..., a...))

"little" |> (*)'''("Mary ")("had ")("a ") |> (*).'(" lamb")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.