dependent types in julia?

286 views
Skip to first unread message

Neal Becker

unread,
Sep 14, 2016, 2:20:57 PM9/14/16
to julia...@googlegroups.com
As a toy example, I'm trying to write something that computes 2nd order
statistics of complex variables.

give a type T :: Complex{FLT}, I need to get the scalar type associated with
it. For example, Complex{Float64} -> Float64

The decomplexify metafunction seems to do it:

decomplexify{T}(::Type{Complex{T}}) = T

julia> decomplexify(Complex{Float64})
Float64

But I don't know how to get this to work:

type var{T}
sum::T
sumsqr::Type{decomplexify{T}}
end

It's trying to say that sum has type e.g., Complex{Float64}, while
sumsqr has type Float64 (here, T would have type Complex{Float64})

Any documents I can read to help?

Evan Fields

unread,
Sep 14, 2016, 3:20:53 PM9/14/16
to julia-users, ndbe...@gmail.com
How about something like the following?

type CT{T}
    ctsum::Complex{T}
    ctsumsq::T
end

x = 1 + 2im

ctx = CT(x, convert(decomplexify(typeof(x)), x * conj(x))

You could also make a convenience function so you don't have to do the converts yourself for the second argument.
Have you seen the standard docs on parametric types? http://docs.julialang.org/en/release-0.5/manual/types/#man-parametric-types

Neal Becker

unread,
Sep 14, 2016, 3:38:53 PM9/14/16
to julia...@googlegroups.com
Evan Fields wrote:

> How about something like the following?
>
> type CT{T}
> ctsum::Complex{T}
> ctsumsq::T
> end
>

I'm aware that it's easier to make the type parameter the scalar type,
allowing writing as you show, but as a learning exercise I'd like to know
how Julia would go the other way.

In c++ this would be done with template metaprogramming, but as Julia claims
to have types as 1st class objects, I thought there should be some elegant
way to do this.

That is, given T is Complex{U}, I need the type U so I can write
ctsumsq::U

Evan Fields

unread,
Sep 14, 2016, 3:59:00 PM9/14/16
to julia-users, ndbe...@gmail.com
I'm not sure then, but hopefully someone here can help you out! Naively perhaps it feels like this makes the type inference impossible. Your function decomplexify always produces the same output for the same input, but what if you had a function that returned a different output (always of type DataType) based on a random number generated inside the function? Then using that function to annotate field types would be super hairy, right?

Dan

unread,
Sep 14, 2016, 6:13:46 PM9/14/16
to julia-users, ndbe...@gmail.com
Maybe the following is the form you are looking for:

julia> decomplexify{T}(::Type{Complex{T}}) = T
decomplexify
(generic function with 1 method)


julia
> type bar{S,T}
           sum
::S
           sumsqr
::T
           
function bar(s,ss)
               
if typeof(ss) != decomplexify(typeof(s))
                   error
("Yaiks")
               
end
               
new(s,ss)
           
end
       
end


julia
> bar{Complex{Float64},Float64}(1.5+2.0im,1.0)
bar
{Complex{Float64},Float64}(1.5 + 2.0im,1.0)


julia
> bar{S,T}(x::S,y::T) = bar{S,T}(x,y)
bar
{S,T}


julia
> bar(1.5+2.0im,1.0)
bar
{Complex{Float64},Float64}(1.5 + 2.0im,1.0)


The outer constructor is necessary to get the last line working. The inner constructor basically maintains the constraint between S and T of: T == Complex{S}.

Isaiah Norton

unread,
Sep 14, 2016, 6:19:00 PM9/14/16
to julia...@googlegroups.com

Neal Becker

unread,
Sep 15, 2016, 8:44:28 AM9/15/16
to julia...@googlegroups.com
OK, I think I got it:

decomplexify{T}(::Type{Complex{T}}) = T

type var2{T,S}
sum::S
sumsqr::T
nobjs::Int64
var2() = new(0,0,0)
end

(::Type{var2{T}}){T}() = var2{T,decomplexify((T))}() << magic?
var2() = var2{Complex{Float64},Float64}() << default

This seems to work
Problem is I have no idea what this line marked "magic" means.

Jeffrey Sarnoff

unread,
Sep 15, 2016, 10:08:22 AM9/15/16
to julia-users, ndbe...@gmail.com
Hi Neal,

It is not good practice to leave out parts of the template that Dan offers.  While it may work with some test data, it is very likely to fail in general use (particularly by/with use from other packages) and it may become incompatible in some specific manner with future releases.

If I get your drift, something like this may serve

underlyingtype{T}(::Type{Complex{T}}) = T

type ComplexOf{S,T} 
    sum::S 
    sumsqr::T
    function ComplexOf{S,T}(s::S,ss::T)
        realtype = underlyingtype(S)
        if typeof(ss) != realtype
            throw(
              TypeError( :ComplexOf, "ComplexOf($s,$ss)\n\t\t", realtype, T ))
        end
        new(s,ss)
     end
end

ComplexOf{S,T}(x::S, y::T) = ComplexOf{S,T}(x,y)

ComplexOf(1.5+2.0im, 1.0)

ComplexOf(5+2im, 3)

# force a type error by mixing the kinds of underlying types
ComplexOf(5.0+1.5im, 2.0f0)

# presumably, you want to add
function ComplexOf{T}(x::Vector{Complex{T}})
    s = sum(x)
    ss = abs2(s)
    return ComplexOf(s,ss)
end


test = ComplexOf([3.0+1.0im, 2.0-1.0im]);
test.sum, test.sumsqr



If you have questions about why something is present, do ask.
Also, in this case, you can use `immutable ComplexOf` rather than `type ComplexOf`, which helps if there are very many of them.

Neal Becker

unread,
Sep 15, 2016, 10:43:09 AM9/15/16
to julia...@googlegroups.com
After some iterations, I'm pretty happy with this form, which doesn't need
any magic:

https://gist.github.com/nbecker/58f79e449400159e4762f6c8eedd9f65

The type var is parameterized by 2 parameters, and the constructors take
care of the type computations, similar to Dan's suggestion. The invariant
checking you suggest could be added (but in my case would be a bit more
complicated)

Jeffrey Sarnoff

unread,
Sep 15, 2016, 10:46:06 AM9/15/16
to julia-users, ndbe...@gmail.com
and if you're pretty happy, that's all!
Reply all
Reply to author
Forward
0 new messages