Want a field signature more specific than 'Function'

I have some predicate types that have a predicate field. I want my fields to be of type (t::T -> Bool). Instead they’re of type Function. It all looks like this:

abstract Foo{T}
  
type unP{T} <: Foo{T}
  p::Function
end
un = unP{Int}(t::Int -> t > 0); println(typeof(un.p))

type andP{T} <: Foo{T}
  p::Vector{Function}
end
and = andP{Int}([t -> t > 0, t -> t < 10]); println(typeof(and.p))
and.p[1](1) |> println

type AndP{T} <: Foo{T}
  p::Tuple{Foo{T},Foo{T}}
end
And = AndP((un,un)); println(typeof(And.p))
And.p[1].p(0) |> println

I searched around, but all I found was how to use Function. Thanks.

There are no arrow types (i.e. function types encoding argument types and return types). Best is probably Jeff’s thesis in explaining why: phdthesis/main.pdf at master · JeffBezanson/phdthesis · GitHub . There is also a bunch of issues on github, e.g. https://github.com/JuliaLang/julia/pull/10269.

As an aside, if your type instances are used in performance critical code, then you should parameterize them on the function type:

type unP{T,F} 
  p::F
end

as then Julia’s type inference will work.

4 Likes

@mauro3, thanks for answer, code tip, and links. I’ll collect all that to help me out.

1 Like

There’s a proof-of-concept implementation of this kind of behavior at GitHub - yuyichao/FunctionWrappers.jl It has some limitations, but it might be a good starting point.

1 Like

I asked about this here

https://groups.google.com/forum/#!searchin/julia-users/protocol$20interface$20method$20signature|sort:relevance/julia-users/kRnIlf9S-iA/lhWbYFsOBgAJ

and @StefanKarpinski said there would likely be some work on such a feature after 1.0. If the feature does come, it won’t be used to control dispatching, more like a constraint check.

1 Like

@bpr, that describes what I’m after, type constraints, along with interfaces for a form of multiple inheritance, all for type enforcement of one kind or another.

@rdeits, thanks. I’m flexible, but for now I prefer to adhere to “don’t add to the default distribution,” except for what I add.

@jameson recently layed out his thoughts on this Proposal for a first-class dispatch wrapper

My understanding is that this might happen for 1.0, but it depends on how much time it takes.

2 Likes

@vchuravy, sounds good. I just use what’s in the language, if I can get enough books, documentation, and help. I’m just starting on some things. There’s plenty of work to do before these features get added.

Below I include a more expanded example, which I worked out because of the type inference tip from @mauro3. I show how I check the signature of a function r in the constructor.

My example leads to two questions, which you may or may not have time to answer.

  1. Because I define an inner constructor, using the inner constructor Foo doesn’t work unless I annotate it with types, as shown below at #(3).
  • My workaround, if I wanted to use the inner constructor, would be to put a dummy variable in the inner constructor, and define an outer constructor that’s the same, except for the dummy var.
  • Is there something I need so that the inner constructor can be used without having to use Foo{Int, Function, Function}?
  1. The runtime check in the constructor for r actually greatly improves the LLVM code, as shown at (1). That’s one more lesson for me, related to this thread, that type inference is everything. This is not really a question, just an observation that may warrant an informative comment from someone.

The code

type Foo{T, F1<:Function, F2<:Function}
  v::T
  R::F1             # F1 :: T -> Bool, domain restriction
  r::Nullable{F2}   # F2 :: () -> T,   random value generator
  function Foo(v::T, R::F1, r::Nullable{F2})
    if !isnull(r) && !(get(r)()|>typeof == T) 
      error("BAD 'r' return type")
    end 
    R(v) ? new(v,R,r) : error("BAD R(v)")
  end
end
function Foo{T, F<:Function}(v::T, R::F)
  Foo{T,F,Function}(v, R, Nullable{Function}())
end
function Foo{T, F1<:Function, F2<:Function}(v::T, R::F1, f2::F2)
  Foo{T,F1,F2}(v, R, Nullable{F2}(f2))
end
#(1) GOOD LLVM: but not if I remove the use of 'r' in the inner constructor.
@code_llvm Foo(1, t::Int -> t > 0, () -> 0)

#(2) DOESN'T WORK: but it will work if I remove the inner constructor.
#Foo(1, t::Int -> t > 0, Nullable{Function}())
  # ERROR: LoadError: MethodError: no method matching 
  # Foo(::Int, ::##5#6, ::Nullable{Function})

#(3) DOES WORK: but I lose good type inference and get bad LLVM.
Foo{Int, Function, Function}(1, t::Int -> t > 0, Nullable{Function}())
@code_llvm Foo{Int, Function, Function}(1, t::Int -> t > 0, Nullable{Function}())