Parametric function return type

I wanted to create a function that takes in input a callback and can return its result while being able to tell Julia what’s going to be the return type (since the Function type is not parametric).

This is the skeleton at the moment:

function call_cb_if_needed(::Type{T}, cb::Function)::T where {T<:Any}
    # if some_condition
    #   return some_value
    # else
    return cb()

call_cb_if_needed(Int, () -> 3)
# 3

This is a paradigm I’m taking with me from JS, I don’t know whether there’s a better way to achieve the same in Julia, but the fact that you can define a parametric Type as an argument of the function looks like it is not documented anywhere, thus I wanted to share it here for anyone that may find this useful.

Suggestions are welcome!

I think this is covered by the manual:
“For each type T , Type{T} is an abstract parametric type whose only instance is the object T . Until we discuss Parametric Methods and conversions, it is difficult to explain the utility of this construct, but in short, it allows one to specialize function behavior on specific types as values .”

This is quite confused as a post, TBH. IMO you shouldn’t mention caching in this post (you can edit the post to remove all references to caching/memoization) to prevent confusing people, but you could make a dedicated post to ask about that.

Regarding the function return type annotation, I’d suggest not to use it, instead just use type assertions in the function body (like some_expression::T, where some_expression is some expression and T is some type), if necessary at all. The problem with putting a return type on a function is that, unintuitively, it doesn’t just do a type assertion, rather it also does a convert if I remember correctly. Usually it doesn’t matter, but the feature seems like something that should be avoided out of principle.

1 Like


I’m not sure I fully understand your goal, but perhaps Memoize.jl is worth taking a look at:

type as argument

the fact that you can define a parametric Type as an argument of the function looks like it is not documented anywhere, thus I wanted to share it here for anyone that may find this useful.

The concepts behind it are documented as greatpet mentions above, and it’s a pretty common pattern you’ll come across in a lot of Julia code, including base Julia itself. But thank you for taking the time to make a post about it, there’s always people new to the language, some of whom might learn about this pattern from this.

I didn’t get the some_expression thing, may you provide an example?

I’ve seen it, but it didn’t look like what I wanted, I’m using the Serialization module to save the output of a long external process in a file, this function checks if the file exists and deserialize it instead of calling the CB (that just calls the external process)

Instead of, for example:

return_int_0(f)::Int = f()

… one could instead write (for type assertion):

return_int_1(f) = f()::Int

… or, something like (for conversion, also see convert):

return_int_2(f) = Int(f())

Note that return_int_1 doesn’t do any conversion of the return value, so it will error if f() doesn’t return an Int value, even if the value is convertible to Int.

It may make sense to do both conversion and a type assertion in some cases, because conversion doesn’t imply an assertion (this is being reconsidered for v2).

1 Like

I see! Interesting. Yeah I feel a little bit annoyed by not being able to explicitly define the return type while avoiding the conversion, I have to manually write it in the function’s docs.

If the function is type-stable and will always return the same type anyway, the convert call will be optimized away. Also, if the expected return type is a user-defined struct type, and you know that there’s no conversion method that can turn other types into this user-defined type, then you don’t need to worry about convert. In such cases, there is no harm in annotating the return type, and it can help document the code.

1 Like