Stream: beginners

Topic: exploring tagged unions


view this post on Zulip Tarjei Skjærset (May 17 2021 at 23:47):

I've probably gone off the deep end here, but I have been having an absolute blast playing with open tagged unions and abusing them for implementing some kind of subtyping. Here I've mocked up a Seq thing where NonEmptySeq is a Seq. Wondering what people think of this kind of thing!

# Basic lazy sequence abstraction,
# using a thunk to calculate the rest.
Seq a : [
        Seq { first : a, rest : ({} -> Seq a) },
        EofSeq
    ]

# A NonEmptySeq always has an element,
# but getting the rest is just a regular seq.
# Because of structural equality, this _is_
# a Seq.
NonEmptySeq a b : [
        Seq { first : a, rest : ({} -> Seq a) }
    ]b

# An infinite seq never has an `EofSeq`
# Because of structural equality, this is
# both a NonEmptySeq and a Seq.
InfiniteSeq a b : [
        Seq { first : a, rest : ({} -> InfiniteSeq a) }
    ]b

# Isn't really correct without List.drop or equivalent.
seqFromList : List a -> Seq a
seqFromList = \ xs ->
    when List.get xs 0 is
        # \ {} -> seqFromList (List.drop xs 1)
        Ok first -> Seq { first, rest: \ {} -> seqFromList xs }
        _ -> EofSeq

# Creates an infinite lazy Seq filled with xs.
repeateInfinite : a -> InfiniteSeq a *
repeateInfinite = \ x ->
    Seq { first: x, rest: \ {} -> repeateInfinite x }

# Length of any Seq
# Dummy implementation, only for testing the types
seqLen : Seq a -> Nat
seqLen = \ _s -> 0

# Gets the head of a NonEmptySeq or an InfiniteSeq
# Always guaranteed to work, no Result here
firstOfNonEmpty : NonEmptySeq a * -> a
firstOfNonEmpty = \ sq ->
    when sq is
        Seq { first } -> first

## Testcases

test : NonEmptySeq Str *
test = Seq { first: "Ikke-tom", rest: \ {} -> EofSeq }

# Can get the length of both Seq and NonEmptySeq even
# though it is implemented in terms of Seq
len : Nat
len = seqLen test

frst : Str
frst = firstOfNonEmpty test

# Can get the first element of an InfiniteSeq, even
# though it's implemented in terms of NonEmptySeq
fsrrst : Str
fsrrst = firstOfNonEmpty (repeateInfinite "Hei")

view this post on Zulip Richard Feldman (May 18 2021 at 02:30):

ha, neat!

view this post on Zulip Richard Feldman (May 18 2021 at 02:31):

in terms of how the values work together, it reminds me of how Num works

view this post on Zulip Richard Feldman (May 18 2021 at 02:31):

the difference being the EofSeq possibility

view this post on Zulip Richard Feldman (May 18 2021 at 02:31):

which Num doesn't have an equivalent of

view this post on Zulip Tarjei Skjærset (May 18 2021 at 07:24):

It's kind of annoying it has to be typed NonEmptySeq a * and InfiniteSeq a * instead of NonEmptySeq a and InfiniteSeq a. Also ditto for the typevariable b in the definitions. Would it not be possible to represent this kind of pattern with a free star in type aliases? :grimacing:

view this post on Zulip Richard Feldman (May 18 2021 at 11:01):

nope, not possible haha :big_smile:

view this post on Zulip Tarjei Skjærset (May 18 2021 at 11:18):

:thinking::thinking::thinking:


Last updated: Jul 06 2025 at 12:14 UTC