# Operators and functions

How come these work

``````==(2).(1:5)
≠(2).(1:5)
>(2).(1:5)
``````

and these don’t ?

``````+(2).(1:5)
*(2).(1:5)
^(2).(1:5)
``````

The syntax `func.(collection)` means applying `func` to the `collection`. For example

``````julia> (x -> 2x).(1:5)
5-element Vector{Int64}:
2
4
6
8
10
``````

In your example, `==(2)`, `≠(2)` and `>(2)` are functions whereas `+(2)`, `*(2)` and `^(2)` are not.

``````julia> ==(2) isa Function
true

julia> +(2) isa Function
false
``````

For more information, `==(2)` is called a partial function application. This is a term from functional programming which means to fix an argument to a function. Specifically, `==(2)` is equivalent to defining `==(x) = y -> x == y` which is equivalent to `Base.Fix2(==, 2)`. See the documentation of `Base.Fix2` for more information about that function.

For readers who wonder why partial function applications are useful, they are mostly useful in combination with higher order functions, that is, functions which take functions such as `map` or `filter`. For example, compare

``````julia> map(x -> x < 2, 1:5)
5-element Vector{Bool}:
1
0
0
0
0

julia> filter(x -> x == 2, 1:5)
1-element Vector{Int64}:
2

julia> filter(s -> contains(s, "ar"), ["foo", "bar"])
1-element Vector{String}:
"bar"
``````

to

``````julia> map(<(2), 1:5)
5-element Vector{Bool}:
1
0
0
0
0

julia> filter(==(2), 1:5)
1-element Vector{Int64}:
2

julia> filter(contains("ar"), ["foo", "bar"])
1-element Vector{String}:
"bar"
``````
3 Likes

Can this notation be used to abbreviate this?

`filter( x-> any( contains.(x,["a","b"]) ), ["aa","bb","cc","dd"] )`

Not that I know of. Sounds like a nice idea though to allow applying AND and OR to functions. What does work here is to move the OR (`any`) inside the `contains`:

``````julia> filter(contains(r"a|b"), ["aa", "bb", "cc", "dd"])
2-element Vector{String}:
"aa"
"bb"
``````
2 Likes

Could this functional form be available for more functions ?
2 examples

It would be nice to write this
`@pipe "A=1,B=2,C=3" |> split(_,',') |> split.(_,'=') `

As
`"A=1,B=2,C=3" |> split(',') .|> split('=')`

``````
``````

And it would be nice to write this

``````conv(x) =
try Date( x, dateformat"dduuuyyyy")     catch
try Currency_Dict[x]                    catch
try Buy_Sell_Dict[x]                    catch
try parse(Float64,x)                    catch
try Symbol(x)                           catch
end end end end
end
``````

as

``````conv(x) = x |>
try Date(dateformat"dduuuyyyy")     catch
try Currency_Dict                   catch
try Buy_Sell_Dict                   catch
try parse(Float64)                  catch
try Symbol                          catch
end end end end
end

``````

The challenging part about this is that this is the following currently return something when the arguments are strings.

``````julia> split(",")
1-element Vector{SubString{String}}:
","

julia> split("=")
1-element Vector{SubString{String}}:
"="
``````

Nonetheless, you correctly identified that `split(::Char)` is not currently defined. Thus we could do:

``````julia> Base.split(c::AbstractChar) = Base.Fix2(Base.split, c)

julia> split(',')
(::Base.Fix2{typeof(split), Char}) (generic function with 1 method)

julia> "A=1,B=2,C=3" |> split(',') .|> split('=')
3-element Vector{Vector{SubString{String}}}:
["A", "1"]
["B", "2"]
["C", "3"]

help?> Base.Fix2
Fix2(f, x)

A type representing a partially-applied version of the two-argument function f, with the second argument fixed to
the value "x". In other words, Fix2(f, x) behaves similarly to y->f(y, x).
``````

A better definition would also allow for the other keyword arguments:

``````Base.split(c::AbstractChar; kwargs...) = Base.Fix2(Base.split, dlm; kwargs...)
``````

The above could be potentially confusing, so I would prefer the following:

``````julia> Base.split(; dlm=isspace) = Base.Fix2(Base.split, dlm)

julia> "A=1,B=2,C=3" |> split(dlm=',') .|> split(dlm='=')
3-element Vector{Vector{SubString{String}}}:
["A", "1"]
["B", "2"]
["C", "3"]
``````

The above does not allow for the other keywords. A keyword compatible version would look like this.

``````julia> Base.split(; dlm=isspace, kwargs...) = x->Base.split(x, dlm; kwargs...)

julia> "a b c" |> split()
3-element Vector{SubString{String}}:
"a"
"b"
"c"

julia> "a b c" |> split(limit=2)
2-element Vector{SubString{String}}:
"a"
"b c"
``````

Hot patching `Base` like this is frowned upon. It might be fine in a terminal project, but can create issues for composability. Another approach is to create your own `split` in a module or package and then import that into your namespace.

``````julia> module SplitFix2
split(c::AbstractChar) = Base.Fix2(Base.split, c)
split(; dlm=isspace, kwargs...) = x->Base.split(x, dlm; kwargs...)
end
Main.SplitFix2

julia> import .SplitFix2: split

julia> "a b c" |> split(limit=2)
2-element Vector{SubString{String}}:
"a"
"b c"

julia> "A=1,B=2,C=3" |> split(dlm=',')
3-element Vector{SubString{String}}:
"A=1"
"B=2"
"C=3"

julia> Base.split == SplitFix2.split
false

julia> @which split
Main.SplitFix2
``````
1 Like

Thanks Mark.

Most functions have one argument that is the data input, and so the one I’d like to be passable from a pipe or chain. Maybe I’ll create my own versions with slightly different names (to avoid the issue you mention with split( ::string ) already being defined ).

``````splt(dlm)       = x -> (split(x,dlm))
fltr(f)         = x -> filter( f, x )
Dat(format)     = x -> Date( x, format)
pars(typ)       = x -> parse(typ,x)
CSV_write(file) = x -> CSV.write( file, x )
``````

I like your version with `split(dlm=',')` as you’re able to keep the same name.