Reimplementing Option and Result types

For those interested in knowing how Option and Result works, here is concise summary
I’m gonna rename Option as Maybe and Result as Either and we gonna have an implementation for map/fmap as well

open Belt

type maybe<'a> = Just('a) | Nothing

let maybeMap =
  (ma: maybe<'a>, f: 'a => 'b) => {
    switch ma {
      | Just(a) => Just(f(a))
      | _ => Nothing
    }
  }

let j1 = Just(1)
let n1 = Nothing

let mj1 = maybeMap(j1, x => Int.toString(x))
let mn1 = maybeMap(n1, x => Int.toString(x))

Js.log(j1)  // { _0: 1 }
Js.log(n1)  // 0
Js.log(mj1) // { _0: '1' }, here we can see that the value was mapped
Js.log(mn1) // 0


type either<'a, 'b> = Left('a) | Right('b)

let eitherMap =
  (ma: either<'a, 'b>, f: 'b => 'c) => {
    switch ma {
      | Right(a) => Right(f(a))
      | Left(a) => Left(a)
    }
  }

let r1 = Right(1)
let l1 = Left(1)

let mr1 = eitherMap(r1, x => Int.toString(x))
let ml1 = eitherMap(l1, x => Int.toString(x))

Js.log(r1)  // { TAG: 1, _0: 1 }
Js.log(l1)  // { TAG: 0, _0: 1 }
Js.log(mr1) // { TAG: 1, _0: '1' }, here we can see that the value was mapped
Js.log(ml1) // { TAG: 0, _0: 1}
2 Likes

Just use Result and Option

1 Like

It just has educational purpose

Then there are some improvement suggestions:

  1. It’s idiomatic to wrap common functions in a module and call the main type as t
module Maybe = {
  type t<'a> = Just('a) | Nothing

  let map =
    (maybe: t<'a>, f: 'a => 'b) => {
      switch maybe {
        | Just(a) => Just(f(a))
        | _ => Nothing
      }
    }
}
  1. There is a more performant way to write matching logic
// Before
switch ma {
      | Right(a) => Right(f(a))
      | Left(a) => Left(a)
    }
// After
switch ma {
      | Right(a) => Right(f(a))
      | Left(_) as left => left
    }

If you compare the output you’ll see that Before we recreated a varian container, while in After we return an existing one.

But still I want to repeat that it’s better to use existing Option and Result modules :slight_smile:

5 Likes