aiken-lang / aiken

A modern smart contract platform for Cardano
https://aiken-lang.org
Apache License 2.0
396 stars 82 forks source link

Union types #957

Closed Fell-x27 closed 1 month ago

Fell-x27 commented 1 month ago

What is your idea? Provide a use case.

We need union types like Haskell provides. For example, I want to extract output values from a list. It can be List<Input> or List<Output>. If I write a unified function like this:

fn extract_utxos(items: List<a>) -> a {
  let utxos =
    when list.head(items) is {
      Some(Input { .. }) -> todo @"extract outputs"
      Some(Output { .. }) -> todo @"use as is"
    }
  todo @"rest"
}

I'll get an error: image

So, I can't determine which type I use. It would be a good idea to check a if it is Input or Output, but Aiken doesn't allow it.

Why is it a good idea?

If we had the ability to do something like:

fn extract_utxos(items: List<Input|Output>) -> a

it would be amazing. It would open up more possibilities to write cleaner, more abstract, and more readable code.

What is the current alternative and why is it not good enough?

The current alternative is defining a custom type, such as:

type InOut {
  In(List<Input>)
  Out(List<Output>)
}

And then match it with your value, but that's not the goal we want to achieve by refactoring and extracting functions. There should be less code and fewer entities, not more. So, it is not good enough.

KtorZ commented 1 month ago

We do already have union types, and the alternative you describe is actually how they also work in Haskell for example.

What you seem to be wanting is something more like Typescript and flow-type systems where you can create types on the fly. That's not generally possible -- or let's say a lot more complicated -- in a type system by unification like Aiken.

Note that you can also simple create a sum type holding generic is you want something reusable. For example:

type Either<a, b> {
  Left(a)
  Right(b)
}

And in your example, you can simply provide a List<Either<a, b>>. We never end up adding any Either or Result type to the prelude or standard lib because they usually encourage traditional error handling patterns which aren't particularly suited for smart contract development where the approach is rather: fail loud and fast.