Last active
March 9, 2023 14:50
-
-
Save kraftdorian/81df8eb25377e171a366c908bbc47291 to your computer and use it in GitHub Desktop.
Maybe typeclass utils in TypeScript
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
type Fun<A, B> = (a: A) => B; | |
class Just<A> { | |
constructor(private readonly _value: A) {} | |
valueOf(): A { | |
return this._value; | |
} | |
} | |
class Nothing { | |
private static readonly id: unique symbol = Symbol('Nothing'); | |
valueOf(): typeof Nothing.id { | |
return Nothing.id; | |
} | |
} | |
type Maybe<A> = Just<A> | Nothing; | |
/** | |
* fmap :: (a -> b) -> Maybe a -> Maybe b | |
*/ | |
function fmap<A, B>(f: Fun<A, B>, a: Maybe<A>): Maybe<B> { | |
return a instanceof Nothing | |
? new Nothing() // fmap f Nothing = Nothing | |
: new Just<B>( // fmap f (Just x) = Just (f x) | |
f(a.valueOf()) | |
); | |
} | |
/** | |
* Maybe Applicative namespace to be able to use string literal function names | |
*/ | |
abstract class Applicative { | |
/** | |
* a -> Maybe a | |
* pure = Just | |
*/ | |
static pure<A>(value: A): Maybe<A> { | |
return new Just<A>(value); | |
} | |
/** | |
* (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b | |
*/ | |
static '<*>'<A, B>(f: Maybe<Fun<A, B>>, a: Maybe<A>): Maybe<B> { | |
return f instanceof Nothing | |
? new Nothing() | |
: fmap<A, B>(f.valueOf(), a); | |
} | |
/** | |
* (<$>) :: (Functor f) => (a -> b) -> Maybe a -> Maybe b | |
* (<$>) = fmap | |
*/ | |
static '<$>'<A, B>(f: Fun<A, B>, a: Maybe<A>): Maybe<B> { | |
return fmap<A, B>(f, a); | |
} | |
} | |
/** | |
* Maybe Monad namespace to be able to use string literal function names | |
*/ | |
abstract class Monad { | |
/** | |
* return :: a -> Maybe a | |
* return = pure | |
*/ | |
static return<A>(value: A): Maybe<A> { | |
return Applicative.pure<A>(value); | |
} | |
/** | |
* (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b | |
*/ | |
static '>>='<A, B>(a: Maybe<A>, f: Fun<A, Maybe<B>>): Maybe<B> { | |
return a instanceof Nothing | |
? new Nothing() | |
: f(a.valueOf()); | |
} | |
} | |
/** | |
* Maybe Monad instance to use in OOP | |
*/ | |
class MonadInst<A> { | |
constructor(private readonly _value: Maybe<A>) {} | |
bind<B>(f: Fun<A, Maybe<B>>): MonadInst<B> { | |
return new MonadInst<B>( | |
Monad['>>='](this._value, f) | |
); | |
} | |
toMaybe(): Maybe<A> { | |
return this._value; | |
} | |
} |
@bebrws I'm glad it's useful, even if it's not perfect - I saw some libs that were covering this and much more.
In general, I've had a few tries at understanding Monads by observing and embedding Haskell's approach into JS/TS:
I've had a great learning experience with the resources below:
Thanks for the feedback 🚀
Awesome, yeah I haven't seen that first link.
That second book is amazing.
I probably didn't really start to wrap my head around them until I started
using Rust and then went back to see how some of the Enums like Result in
Rust are similar to monads in other languages.
Appreciate the great code and helpful information!
…On Wed, Mar 8, 2023 at 3:52 PM Damian ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
@bebrws <https://github.com/bebrws> I'm glad it's useful, even if it's
not perfect - I saw some libs that were covering this and much more.
In general, I've had a few tries at understanding Monads by observing and
embedding Haskell's approach into JS/TS:
- monad.ts
<https://gist.github.com/kraftdorian/b82fa095a698fc0047a5ecb2914361b2>,
- monads.js
<https://gist.github.com/kraftdorian/f6caa56c474b00fdeec8d115d6f0c78e>,
- ts-maybe <https://github.com/kraftdorian/ts-maybe>.
In both cases, I've had a great learning experience with the resources
below:
- Eric Elliott's "JavaScript Monads Made Simple"
<https://medium.com/javascript-scene/javascript-monads-made-simple-7856be57bfe8>
- Learn You a Haskell for Great Good! <http://learnyouahaskell.com>
Thanks for the feedback 🚀
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/81df8eb25377e171a366c908bbc47291#gistcomment-4496027>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AMI2QU3KJJNR6RCGJBT7KELW3ELT7BFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFQKSXMYLMOVS2I5DSOVS2I3TBNVS3W5DIOJSWCZC7OBQXE5DJMNUXAYLOORPWCY3UNF3GS5DZVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTCMZTGMYDEOJZU52HE2LHM5SXFJTDOJSWC5DF>
.
You are receiving this email because you were mentioned.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you! Love this implementation. Just a note to myself about how it could be used: