Classification of functions

question

#1

I would like to process some functions according
to the number of their parameters and with regard
to the return value boolean.

function G(f::Function)
  ! hasparameters(f) && return 0
  returnsboolean(f)  && return 1
  return 2
end

How can the query functions "hasparameters"
and “returnsboolean” be implemented?

Assume VERSION >= 0.6.

Edit: ChrisRackauckas’ solution seems to work for my purposes:

hasparameters(f) = 0 < maximum((length(m.sig.parameters) for m in methods(f))...)
returnsboolean(f) = first((methods(f)...)).specializations.func.rettype == Bool

function G(f::Function)
    returnsboolean(f) && return 1 # query functions with parameters
    hasparameters(f)  && return 2 # functions with parameters
    return 0 # functions with no parameters
end

Ability to define function templates
#2

length(typeof(f).parameters > 0)

Return type can be dependent on the input type, so this is ill-defined the way you have it.


#3

This will be false for all normal functions and is unlikely what he’s looking for (though I’m not sure what that is either…)


#4

Oh yes, if it’s defined as a normal function then it won’t have parameters. But callable types can. Not sure what else could be meant by this though. Number of arguments? That’s not well-defined either since you can have multiple methods, but this would work based on the maximum number:

function hasparameters(f)
  numparam = maximum((length(m.sig.parameters) for m in methods(f))...)
  numparam > 0
end

#5

function hasparameters(f)

Yes this seems to do what I want.

Return type can be dependent on the input type, so this is ill-defined the way you have it.

What I want to catch here are simple query functions like isprime, iseven, iszero etc. So I assume the return type is well defined as Bool. In any case am happy with a solution which works only in these cases (and possibly not in more general setups).


#6
returnsboolean(f) = first((methods(f)...)).specializations.func.rettype == Bool

That just uses the first method. Not sure how robust that is.


#7

This will not tell you what you want. It can only tell you if a method has been compiled that could return no wider than Bool.


#8

I think it depends. With ‘using Primes’ and ‘isprime’ I get indeed a “LoadError: type Void has no field func”. Is it this what you means?

However it will work if I define the query function more explicite as

  function Isprime(n)::Bool isprime(n) end

Or do you think even this could bring troubles?


#9

Well, that one is basically returning random values so it is not guarantee to work in almost all cases…


#10

Then I do not understand you. In my small program it works so far in all cases.


#11

It’s deterministic so if it work in one run it should work in another run. If you want a function that returns true for the case you want it to or throw an error then sure it can be used like that. Otherwise it will not at all tell you what you want.

You should also describe the issue you want to solve more clearly. In julia there is not static return type, especially not for functions instead of methods so the classification you are talking about is not very well defined to begin with.


#12

You should also describe the issue you want to solve more clearly.

That’s why I posted in the category “First Steps”. It may be a long time since you were a beginner, so I bring to your memory: not to be able to describe the problem clearly is an integral part of the difficulty with which a novice is struggling.

In julia there is not static return type, especially not for functions instead of methods so the classification you are talking about is not very well defined to begin with.

OK.

It’s deterministic so if it work in one run it should work in another run.

Well, this is basically what I want. Thanks.


#13

What I mean is that you are likely trying to solve a problem in the wrong way so you should post what you actually want to do at a higher level.

I’m 100% sure it isn’t in this case. This is not better than a hard coded dict.


#14

Also, if you don’t have a higher level description, you should at least give examples of the category of functions that you want to differentiate.
For example, your current version does this on 0.6.

julia> G(()->true)
ERROR: type Void has no field func
Stacktrace:
 [1] G(::Function) at ./REPL[3]:2

julia> G(()->false)
ERROR: type Void has no field func
Stacktrace:
 [1] G(::Function) at ./REPL[3]:2

julia> hasparameters(sin)
ERROR: type UnionAll has no field parameters
Stacktrace:
 [1] append_any(::Base.Generator{Base.MethodList,##1#2}, ::Vararg{Base.Generator{Base.MethodList,##1#2},N} where N) at ./essentials.jl:160
 [2] hasparameters(::Function) at ./REPL[1]:1

#15

I’ll just add that there is no official way to do this, and calling methods will be extremely slow. This is basically a reflection-based approach, meaning you are digging in to system internals to find something out that’s not part of “normal” program flow.


#16

I’ve applied the tricks of Chris now many times and it
has always worked. That it is ‘slow’ was never a problem.

Nevertheless, I do realize now that it is not quite the
Julian way to do things. So I’m still interested in
alternative implementations.

Basically I use them as a switch on the ‘type’ of function.
I give an example:

function g(A, f)
    "fill the array A by using method f"
    switch(f) : 
        if f returns a boolean do ... 
        if f returns an int do ...
        etc...
    end_switch
    return A
end

So what is the Julian way to achive this?


#17

Pass in the type explicitly.