Поскольку вы, кажется, раньше использовали Haskell, я буду адаптировать этот ответ с точки зрения функционального программирования. Общим примером использования самореферентных неизменяемых типов является создание ленивого списка.
Как строгий (то есть не ленивый) язык, невозможно, чтобы неизменный объект непосредственно ссылался на себя.
Это не исключает, однако, косвенное косвенное использование, используя изменяемый объект, такой как Ref
или Vector
.
Для конкретного случая ленивых структур я мог бы рекомендовать ограничить изменчивость специальным объектом, скажем, Lazy{T}
. Например,
import Base: getindex
type Lazy
thunk
value
Lazy(thunk) = new(thunk)
end
evaluate!(lazy::Lazy) = (lazy.value = lazy.thunk(); lazy.value)
getindex(lazy::Lazy) = isdefined(lazy, :value) ? lazy.value : evaluate!(lazy)
Тогда, например, можно сделать простой ленивый список выглядит следующим образом:
import Base: first, tail, start, next, done, iteratorsize, HasLength, SizeUnknown
abstract List
immutable Cons <: List
head
tail::Lazy
end
immutable Nil <: List end
macro cons(x, y)
quote
Cons($(esc(x)), Lazy(() -> $(esc(y))))
end
end
first(xs::Cons) = xs.head
tail(xs::Cons) = xs.tail[]
start(xs::Cons) = xs
next(::Cons, xs) = first(xs), tail(xs)
done(::List, ::Cons) = false
done(::List, ::Nil) = true
iteratorsize(::Nil) = HasLength()
iteratorsize(::Cons) = SizeUnknown()
Что действительно работает, как это было бы в таком языке, как Haskell:
julia> xs = @cons(1, ys)
Cons(1,Lazy(false,#3,#undef))
julia> ys = @cons(2, xs)
Cons(2,Lazy(false,#5,#undef))
julia> [take(xs, 5)...]
5-element Array{Int64,1}:
1
2
1
2
1
Эта функциональность может показаться сложной, но, к счастью, она уже реализована в Lazy.jl.
Важно отметить, что приведенный выше код создает много накладных расходов из-за нестабильности типа и изменяемых типов. Если ваша цель в использовании immutable
не была выразительностью, а скорее производительностью, то, очевидно, такой подход не подходит. Но, как правило, невозможно создать структуру, распределенную по стеку, поэтому в тех случаях, когда требуется максимальная производительность, лучше всего полностью исключить самооценку.
Думаю, вы ответили на свой вопрос - вы не можете. –
Кроме того, зачем вам это нужно? –
@David, чтобы создать исполняемые, неизменные рекурсивные структуры данных. Единственный способ, которым я это знаю, - это абстрактные типы, которые должны принести штраф за производительность. Что я не понимаю, что в концепции неизменности типа должно запрещать самореферентное неизменное? – Bernd