Skip to content

Instantly share code, notes, and snippets.

@ceedubs
Last active September 15, 2021 15:10
Show Gist options
  • Save ceedubs/ce02f577dc60c7857cac03d3c24527f1 to your computer and use it in GitHub Desktop.
Save ceedubs/ce02f577dc60c7857cac03d3c24527f1 to your computer and use it in GitHub Desktop.
Unison runtime crash: applying non-function: Enum ##Boolean 1

background: data structures and functions

{{
A data structure that holds the arguments that you would pass to foldLeft.

The one difference is that there is an extra "extract" function at the end that is to be run after
the foldLeft completes. This can be useful when the fold needs to maintain some state during the
fold but you don't want to include it in the final result.
}}
unique type Fold' g a b x = Fold' (x -> {g} a -> {g} x) x (x -> {g} b)

{{
This is essentially a {Fold'} where the `x` type parameter is an existential type.

Because Unison doesn't directly support existential types, there is a bit of noise required to
craft them from universal type parameters.
}}
unique type Fold g a b = Fold (∀ g2 r. (∀ x. Fold' g a b x -> {g2} r) -> {g2} r)

Fold.fromFold' : Fold' g a b x -> Fold g a b
Fold.fromFold' fold = Fold.Fold (f -> f fold)

{{ Helper function for creating a {type Fold} without manually constructing a {type Fold'}}}
Fold.mkFold : (t -> {g} a -> {g} t) -> t -> (t -> {g} b) -> Fold g a b
Fold.mkFold step init extract =
  Fold.fromFold' (Fold'.Fold' step init extract)

{{ Check whether the predicate holds for all elements }}
folds.all : (a -> {g} Boolean) -> Fold g a Boolean
folds.all predicate =
  Fold.mkFold (b -> a -> b && (predicate a)) true id

{{ Run a fold on a {type .base.Stream}}}
Fold.Stream.fold : Fold g a b -> '{g, Stream a} r -> '{g} b
Fold.Stream.fold =
  run: Fold' g a b x -> '{g, Stream a} r -> '{g} b
  run =
    cases Fold'.Fold' step init extract -> stream -> _ -> extract !(.base.Stream.fold step init stream)
  cases
    Fold f -> stream -> f (f' -> run f' stream)
.> add

the error

Now the following code compiles, but when run via test> results in the runtime error applying non-function: Enum ##Boolean 1:

folds.all.tests.stream =
  pred = n -> (n Nat.> 2)
  res : 'Boolean
  res = Fold.Stream.fold (folds.all pred) (Stream.range 1 5)
  check (!res Universal.== false)

similar code that runs fine

However, similar code using List works just fine. So it would seem that the issue arises somewhere with abilities and/or delayed thunks.

Fold.List.fold : Fold g a b -> [a] -> {g} b
Fold.List.fold =
  run: Fold' g a b x -> [a] -> {g} b
  run =
    cases Fold'.Fold' step init extract -> as -> extract (.base.List.foldLeft step init as)
  cases
    Fold.Fold f -> as -> f (f' -> run f' as)

test> folds.all.tests.list =
  pred = n -> (n Nat.> 2)
  res : Boolean
  res = Fold.List.fold (folds.all pred) [1, 2, 3, 4, 5]
  check (res Universal.== false)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment