Last active
September 17, 2019 20:17
-
-
Save fvilante/6ce13329bf307d307a040a63b70fe560 to your computer and use it in GitHub Desktop.
Pattern Match
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Maybe_ { | |
type Undefined = typeof Undefined | |
const Undefined = Symbol.for("None") // undefined | |
interface Just<T> { | |
readonly type: "Just", | |
readonly val: T | Undefined | |
} | |
interface None<T> { | |
readonly type: "None", | |
readonly val: T | Undefined | |
} | |
export interface Maybe<T> { | |
readonly type: "Maybe" | |
readonly val: Just<T> | None<T> | |
} | |
export const Just = <T>(val: T ): Maybe<T> => | |
({ type: "Maybe", val: { type: "Just", val } }) | |
const None_ = <T>(val: T ): Maybe<T> => | |
({ type: "Maybe", val: { type: "None", val } }) | |
export const None = None_<Undefined>(Undefined) | |
// Teste inferencia implicita deste maybe foi | |
// omitida por motivos de brevidade. mas pode | |
// ser vista aqui: | |
// [link](https://gist.github.com/fvilante/2f53b699794c1ac35e087872e0acac98#file-functional-core-ts-L27) | |
} | |
namespace PatternMatch { | |
import Just = Maybe_.Just | |
import None = Maybe_.None | |
import Maybe = Maybe_.Maybe | |
type InferMaybeType<T> = T extends Maybe<infer U> ? U : never | |
type Maybe_<T> = Maybe<InferMaybeType<T>> // helper for brevity | |
type GetMaybeConstructors<T extends Maybe_<T>> = T['val']['type'] | |
type CallBack<T, U> = (value: T) => U | |
type Match<T extends Maybe_<T>> = { | |
[Constructor in GetMaybeConstructors<T>]: CallBack< InferMaybeType<T>, any > | |
} | |
const match = <T extends Maybe_<T>>(maybe: T, match_: Match<T>) => { | |
const typeOfMaybeConstructor = maybe.val.type | |
const valueOfMaybe = maybe.val.val | |
return match_[typeOfMaybeConstructor](valueOfMaybe) | |
} | |
// ********************************************* | |
// USO: | |
function plus_one(x: Maybe<number>): Maybe<number> { | |
return match(x, { | |
None: () => None, | |
Just: (i) => Just(i+1) | |
}) | |
} | |
const five = Just(5) | |
const six = plus_one(five) | |
const none_ = plus_one(None) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Advantages of using pattern match
Pattern match is a feature of strong typed functional languages which main advantages are:
What happens if you forget to handle a particular case of your algorithm logic?
Pattern match assures that you will handle all paths of your programming logic. And if you do not, it will altert you at compile-time (or in wrinting-time if you use Linter). For example:
Aboce code will pass without raise any compile-time error:
Inspiration
Our inspiration comes from well-typed functional languages like Haskell, Rust, F#. Particularly from this two articles Rust Option and F# Option. Both articles include pattern matching mechanism concepts.
Constraints and future actions
This implementation is just a prove-of-concept. At moment It just works with
Maybe
type, but we intend to generalize the function to work with any Monad like Either, IO, or even an user-defined Monad. All contribution are welcome!Conceptual framework
We are depending of two concepts that Typescript's team introduced recently:
Those two concepts are used in the line of code that says:
type InferMaybeType<T> = T extends Maybe<infer U> ? U : never
And in Generics in general. See:
OBS In line codes 43 and 44 we use a specific kind of access (ie: the
T['val']['type']
part ) that I din't find any reference in TS documentation, but in an Stackoverflow anwser: sorry don't remember de linkAcknoledge
Thanks to Vitor Luiz Cavalcant to sugest Rust as inspirantion and to challange me.