Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maybe a type in typescript

I would like to construct a Maybe a type in typescript a la Haskell:

data Maybe a = Just a | Nothing

It seems like the way to do it in typescript is:

interface Nothing { tag "Nothing }
type Maybe<T> = T | Nothing

I would like to make a function:

function foo(x : string) : Maybe<T> {
    return Nothing
}

akin to:

foo : String -> Maybe a
foo _ = Nothing

However this does not work in typescript. What is the right way to return a value Nothing in typescript? I would like to avoid using null if possible.

___________________________________________________-

Edit: It would be really nice if the function foo would return a value Nothing because I would like to pattern match on value constructor later, ie:

case blah blah of 
    | Just x -> x + x
    | Nothing -> "no words"
like image 290
xiaolingxiao Avatar asked May 28 '18 22:05

xiaolingxiao


2 Answers

Depending on the case, it can be void, undefined, or ? optional modifier for properties and parameters.

It's:

function foo(x : string) : number | void {
    // returns nothing
}

void and undefined types are compatible, but there is some difference between them. The former is preferable for function return types, because the latter requires a function to have return statement:

function foo(x : string) : number | undefined {
    return;
}

Maybe can be implemented with generic type. Explicit Nothing type can be implemented with unique symbol:

const Nothing = Symbol('nothing');
type Nothing = typeof Nothing;
type Maybe<T> = T | Nothing;

function foo(x : string) : Maybe<number> {
    return Nothing;
}

Or a class (private fields can be used to prevent ducktyping):

abstract class Nothing {
    private tag = 'nothing'
}
type Maybe<T> = T | typeof Nothing;

function foo(x : string) : Maybe<number> {
    return Nothing;
}

Notice that class types designate class instance type and require to use typeof when a class is referred.

Or an object (if duck typing can be desirable):

const Nothing: { tag: 'Nothing' } = { tag: 'Nothing' };
type Nothing = typeof Nothing;
type Maybe<T> = T | Nothing;

function foo(x : string) : Maybe<number> {
    return Nothing;
}
like image 102
Estus Flask Avatar answered Nov 09 '22 14:11

Estus Flask


enum Nothing {
  Nothing = 'Nothing'
}

type Maybe<A> = A | Nothing
type MaybeNumber = Maybe<number>

const isNothing = (m: MaybeNumber): m is Nothing => m === Nothing.Nothing 

const foo = (m: MaybeNumber) => {
  if (isNothing(m)) {
    return Nothing.Nothing
  } else {
    return m + 1
  }
}

foo(1) // ✔️
foo('Nothing') // ❌
foo(Nothing.Nothing) // ✔️

or without using an enum using a string literal type

const Nothing = '__nothing__'

type Maybe<A> = A | Nothing
type MaybeNumber = Maybe<number>

const isNothing = (m: MaybeNumber): m is Nothing => m === Nothing 

const foo = (m: MaybeNumber) => {
  if (isNothing(m)) {
    return Nothing
  } else {
    return m + 1
  }
}

foo(1) // ✔️
foo(Nothing) // ✔️
foo('__nothing__') // ✔️

or with symbols

const nothing = Symbol('Nothing')
type Nothing = typeof nothing

type Maybe<A> = A | Nothing
type MaybeNumber = Maybe<number>

const isNothing = (m: MaybeNumber): m is Nothing => m.toString() === 'Symbol(Nothing)' 

const foo = (m: MaybeNumber) => {
  if (isNothing(m)) {
    return nothing
  } else {
    return m + 1
  }
}

foo(1) // ✔️
foo(nothing) // ✔️

live on stackblitz

like image 20
Mateja Petrovic Avatar answered Nov 09 '22 13:11

Mateja Petrovic