Skip to content

Commit

Permalink
Merge pull request #1 from jw3126/kw
Browse files Browse the repository at this point in the history
make bind work with keyword functions
  • Loading branch information
goretkin authored Jun 18, 2020
2 parents ca0d343 + 3736988 commit 4d9c9d9
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 25 deletions.
60 changes: 37 additions & 23 deletions src/Curry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export Bind, @bind
Return a `Tuple` that interleaves `args` into the `nothing` slots of `slots`.
```jldoctest
using Curry
Curry.interleave((:a, nothing, :c, nothing), (12, 34))
# output
Expand All @@ -17,6 +18,7 @@ Curry.interleave((:a, nothing, :c, nothing), (12, 34))
Use `Some` to escape `nothing`
```jldoctest
using Curry
Curry.interleave((:a, Some(nothing), :c, nothing), (34,))
# output
Expand All @@ -41,40 +43,52 @@ _interleave(firstbind::T, tailbind::Tuple, args::Tuple) where T = (

"""
Represent a function call, with partially bound arguments.
"""
struct Bind{F, A, K} <: Function
f::F
a::A
k::K
end

function (c::Bind)(args...; kw...)
c.f(interleave(c.a, args)...; c.k..., kw...)
end

"""
`bind(f, a, b)`
`bind(f, args...; kw...)`
The `bind` function partially evaluates `f` by binding some of its arguments.
Positional arguments of `f` that should not be bound are indicated by passing `nothing`
to `bind` at the respective position.
```jldoctest
b = Bind(+, (1, 2))
b()
julia> using Curry: bind
# output
julia> b = bind(+, 1, 2); # no nothing, all arguments bound
julia> b()
3
```
```jldoctest
b = Bind(*, ("hello", nothing))
b(", world")
# output
julia> b = bind(*, "hello", nothing); # only first argument bound
julia> b(", world")
"hello, world"
```
`Bind(f, (g(), h()))` is like `:(f(g(), h()))` but `f` `g` and `h` are lexically scoped, and `g()` and `h()` are evaluated eagerly.
"""
struct Bind{F, A} <: Function
f::F
a::A
end
julia> b = bind(=>, nothing, 1); # second argument bound
function (c::Bind)(args...)
c.f(interleave(c.a, args)...)
end
julia> b("one")
"one" => 1
julia> b = bind(isapprox, nothing, nothing, atol=100); # only atol keyword bound
julia> b(10, 20)
true
julia> b(10, 20, atol=1) # keywords can be reassigned on the fly
false
```
"""
`bind(f, a, b)` is equivalent to Bind(f, (a, b))
"""
bind(f, args...) = Bind(f, args)
bind(f, args...; kw...) = Bind(f, args, kw)

"""
`@bind f(a,b)` macroexpands to `bind(f, a, b)`
Expand All @@ -83,7 +97,7 @@ bind(f, args...) = Bind(f, args)
macro bind(ex)
ex.head == :call || error()
# `ex.args[1]` is the function and `ex.args[2:end]` are the positional arguments
return :(bind($(map(esc, ex.args)...)))
return :($bind($(map(esc, ex.args)...)))
end

end
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[deps]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
29 changes: 27 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
using Curry
using Curry: bind
using Test


@testset "keywords" begin
@test bind(, 1, nothing, atol=1.1)(2)
@test bind(, 1, nothing, )(2, atol=1.1)
@test !bind(, 1, nothing, atol=1.1)(2, atol=0.9)
@test !bind(, 1, nothing, atol=0.9)(2)
@test bind(, 1, nothing, atol=0.9)(2, atol=1.1)
@test bind(, 1, nothing, atol=0.9)(2, atol=2)

@test bind(, nothing, nothing, atol=1.1)(1,2)
@test !bind(, nothing, nothing, atol=0.9)(1,2)
a2 = @inferred bind(, nothing, nothing)
b2 = @inferred bind(, nothing, nothing, atol=1)
c2 = @inferred bind(, nothing, nothing, atol=1, rtol=2)
a1 = @inferred bind(, 3, nothing, atol=1, rtol=2)
@inferred a2(1,2)
@inferred b2(1,2)
@inferred c2(1,2)
@inferred a1(1.0)
end

@testset "Bind.jl" begin

#=
Expand Down Expand Up @@ -40,13 +62,16 @@ using Test
@test eager == lazy


is3 = Bind(==, (3, nothing))
is3 = bind(==, 3, nothing)
@test false == @inferred is3(4)
isnothing2 = Bind(===, (Some(nothing), nothing))
isnothing2 = Bind(===, (Some(nothing), nothing), ())

@test isnothing2(nothing)
@test isnothing2(:notnothing) == false

b = @bind "hey" * nothing * "there"
@test b(", you, ") == "hey, you, there"
end

using Documenter: doctest
doctest(Curry)

0 comments on commit 4d9c9d9

Please sign in to comment.