Variable number of parameters types in a function

419 views
Skip to first unread message

data.pu...@gmail.com

unread,
Oct 3, 2015, 10:22:49 PM10/3/15
to julia-users
Is it possible to specify a variable number of parameter types in Julia, for instance something like

function {T...}my_fun(x...)
  for i in 1:length(x)
    println(typeof(x[i]) <: T[i])
  end
end

should print true ... times

Thanks

Tom Breloff

unread,
Oct 3, 2015, 11:30:19 PM10/3/15
to julia-users
I think maybe the function you're looking for is something like:

function my_fun(::Type{T}, x...)
  for xi in x
    println(typeof(xi) <: T)
  end
end

or some variation of that?

data.pu...@gmail.com

unread,
Oct 4, 2015, 4:45:19 PM10/4/15
to julia-users
Hi Tom,

I am looking for something analogous to a C++ variadic template function where the types as well as the arguments to the function are varags. These are aligned so that in my above notation T... would be a tuple/array of types corresponding to the types of x... and so each T[i] could be arbitrary.



DP

David P. Sanders

unread,
Oct 4, 2015, 5:29:06 PM10/4/15
to julia-users


El domingo, 4 de octubre de 2015, 15:45:19 (UTC-5), data.pu...@gmail.com escribió:
Hi Tom,

I am looking for something analogous to a C++ variadic template function where the types as well as the arguments to the function are varags. These are aligned so that in my above notation T... would be a tuple/array of types corresponding to the types of x... and so each T[i] could be arbitrary.

You can just use a tuple of types:

julia> function my_fun(types, xx)
         for (T, x) in zip(types, xx)
           println(typeof(x) <: T)
         end
       end
my_fun (generic function with 1 method)

julia> my_fun((Float64, Int), (0.3, 1))
true
true

David Gold

unread,
Oct 4, 2015, 7:03:33 PM10/4/15
to julia-users
Note that if the type variable T does not appear in the argument signature then the method won't be callable.

If you really want to make use of the variable parameters, your best bet I think is to pass a tuple of args:

function f{T<:Tuple}(x::T)
    for (i, param) in enumerate(T.parameters)
        println(typeof(x[i]) <: param)
    end
end

This (or some variant, I forget) can also make the types of the individual xs available at compile time. 

data.pu...@gmail.com

unread,
Oct 4, 2015, 7:32:57 PM10/4/15
to julia-users
Thanks David, that gets me part of the way there but is there a way of specifying the x argument as a varag so that the function can be called as:

f(1, 2, 3) rather than f((1, 2 , 3)) this is useful if I am trying to "lift" a bunch of functions that already exist to play with new types. For instance I could then call plus on the new types T1(value1) + T2(value2) retaining the natural semantics of the functions.

DP

Glen O

unread,
Oct 5, 2015, 2:20:55 AM10/5/15
to julia-users
Just to be clear, are you looking for something that will parameterise across a variety of types, or are you wanting to restrict to a single type?

If the latter, it can be easily done with

function mycode{T}(x::T...)
   <code here>
end

which will make all values in the varargs list the same. To restrict to a specific subset, there's also

function mycode(x::Union(Int,Array)...)
  <code here>
end

which will require that the arguments in x be either Ints or Arrays (and can have both).

If you're actually after the former, is there a reason why you can't just ask for typeof(x[i]), directly? Alternatively, would this work?

function mycode{T}(x...;t::T=x)
    for j=1:length(x)
      println(typeof(x[j])<:T[j])
    end
end

data.pu...@gmail.com

unread,
Oct 5, 2015, 4:30:48 AM10/5/15
to julia-users
Hi Glen,


Perhaps in my previous email when I wrote:

f(1, 2, 3) rather than f((1, 2 , 3))

I should have written something like f(1, 2.3, 5 + 3.im, 3 + 1im, Nullable{Float64}(5.)) to make it explicit that I would like to parametrise different types. I think David Gold's suggestion is the closest to what I want but doesn't quite solve the problem. If I wanted to lift a set of Julia's functionality to new types having an arbitrary set of input types, I could:

1. Spend alot of time writing code to parse different subsets of modules. This would take an immense amount of work and the libraries would inevitably change.
2. If we could model a variadic template function having the semantics as I specified previously:


function {T...}my_fun(x...)
  for i in 1:length(x)
    println(typeof(x[i]) <: T[i])
  end
end

The above function for simplicity. Here T... are the types of x... and they could be all different or the same

We could lift functionality of all the relevant function in one go with a sketch implementation like:

# Import the functions ...
import Base: get, +, -, /, *, ^, ...

# Function symbols to be lifted
funs = [:+, :-, ...]

# Generic get function
# Assume appropriate get functions are written for each type
function get{T}(x::T)
  x
end

# My new type is T1{T}()

S = Union{Symbol, Expr}

# My lifter function
function lift(fun_name::S, new_type::S)
  quote
    function $fun_name{T...}(x...)
    y = Array(Any, length(x))

    for i in 1:length(x)
      y[i] = get(i)
    end
    ret = $fun_name(y...)
    U = typeof(ret)
    return $new_type{U}(ret)
    end
  end
end

# Then lift the functions
for i in funs
  eval(lift(i, :T1))
end

Could then do T1(3.4) + 9 and so on for all the functions regardless of number of arguments return type etc

Glen O

unread,
Oct 5, 2015, 6:31:06 AM10/5/15
to julia-users
I'll admit, I didn't entirely follow what you're trying to do, but based on what I was able to see, is there any reason why my last suggestion wouldn't work? I'll copy it here:


function mycode{T}(x...;t::T=x)
    for j=1:length(x)
      println(typeof(x[j])<:T[j])
    end
end

By making t take the value of x, you are allowing t to have the same "type" as x - namely, a tuple containing the input arguments. Then, by having t::T, it makes T hold their types. And because it's a keyword argument, it can be placed after x..., and doesn't need to be input at all, because it's defaulted to x. It certainly satisfies the requirements you put forward in your original question.

data.pu...@gmail.com

unread,
Oct 5, 2015, 6:51:20 AM10/5/15
to julia-users
Sorry, I ran your code and got an error so assumed it wouldn't work, but I think I see your point. If we modify it a bit:

function mycode2{T}(x...;t::T=x)
    for j=1:length(x)
      println(x[j])
    end
    println(T)
  return(T)
end

This: mycode2(2, 4., 4 + 5im, 4. + 2im) returns:

Tuple{Int64,Float64,Complex{Int64},Complex{Float64}}

and prints the correct values. So how do I access the types in Tuple{} since T[i] will give errors?

data.pu...@gmail.com

unread,
Oct 5, 2015, 6:53:36 AM10/5/15
to julia-users
I see we can do typeof(x[j])

data.pu...@gmail.com

unread,
Oct 5, 2015, 6:55:51 AM10/5/15
to julia-users
p.s. Thanks Glen!

Tomas Lycken

unread,
Oct 5, 2015, 6:56:54 AM10/5/15
to julia-users

In this case, typeof(x[j]) is probably preferable, but if you have access only to the type and not the values, you can also use T.parameters[j] to the same end.

// T

data.pu...@gmail.com

unread,
Oct 5, 2015, 7:17:50 AM10/5/15
to julia-users
Thanks Thomas so we can modify Glen's code as:

function mycode3{T}(x...;t::T=x)
    for j=1:length(x)
      println(typeof(x[j])<:T.parameters[j])
    end
end

and mycode3(2, 4., 4 + 5im, 4. + 2im) will print:

true
true
true

Glen O

unread,
Oct 5, 2015, 7:19:34 AM10/5/15
to julia-users
Perhaps the distinction is due to 0.3 vs 0.4 - I'm still using 0.3 until the final release, because I don't want to go through the hassle of manual installation when the PPA will be updated as soon as 0.4 is officially released. The code worked perfectly for me. Anyway, Tomas has provided the way to access it, it seems, so it should work - just a bit more complicated along the way.

data.pu...@gmail.com

unread,
Oct 5, 2015, 7:26:51 AM10/5/15
to julia-users
I am on Version 0.5.0-dev+433 (2015-09-29 15:39 UTC) and probably should have mentioned that!

data.pu...@gmail.com

unread,
Oct 5, 2015, 7:35:07 AM10/5/15
to julia-users
Probably preaching to the choir but the PPA for nightly builds are available: https://launchpad.net/~staticfloat/+archive/ubuntu/julianightlies ... so exciting when I update. You never know what's going to change!


On Monday, October 5, 2015 at 12:19:34 PM UTC+1, Glen O wrote:

Tomas Lycken

unread,
Oct 5, 2015, 7:37:09 AM10/5/15
to julia-users
Yes, the distinction between `T[i]` on 0.3.x and `T.parameters[i]` on 0.4- and up is because of a change to tuples that took place in the 0.4-dev cycle; before the change, `typeof((3.5, 2)) == (Float64, Int)`, i.e. the type of a tuple was a tuple of the types of the elements, but after the change tuples got their own type and now `tuple((3.5,2)) == Tuple{Float64, Int}`. (This was so disruptive to on-the-edge packages it was nick-named "The Tuplocalypse"...)

// T

Jameson Nash

unread,
Oct 5, 2015, 12:28:59 PM10/5/15
to julia...@googlegroups.com
This seems to be an unnecessarily obfuscated implementation of typeof. Julia already knows how to deal with `typeof(x[j])` very efficiently, while it doesn't understand any of `typeof(x).parameters[j]`, or `T.parameters[j]`, or `t::T=x` particularly well.
Reply all
Reply to author
Forward
0 new messages