State Monad in TypeScript
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
5
down vote
favorite
Here's my implementation of State Monad in TypeScript, based on a canonical Haskell implementation.
I would like it to get code reviewed.
class StateMonad<S, A>
constructor(public runState: (s: S) => ( s: S, a: A ))
static return_<S, A>(a: A): StateMonad<S, A>
return new StateMonad(s => ( s, a ));
bind<B>(func: (a: A) => StateMonad<S, B>): StateMonad<S, B>
return new StateMonad<S, B>((s: S) =>
const s: s_, a = this.runState(s);
return func(a).runState(s_);
);
// aux monad factory
const createCounter = (regex: RegExp) => new StateMonad((s: string) =>
s.split('')
.reduce((acc, c) =>
(regex.test(c)) ? s: acc.s.replace(c, ''), a: acc.a + 1 : acc,
s, a: 0 )
);
const countLowerCase = createCounter(/[a-z]/);
const countDigits = createCounter(/[0-9]/);
// usage example
const a = countLowerCase /* -- haskell equivalent */
.bind(n1 => countDigits /* do n1 <- countLowerCase */
.bind(n2 => StateMonad /* n2 <- countDigits */
.return_(n1 + n2))) /* return n1 + n2 */
.runState("abc123ABC");
typescript monads state
 |Â
show 1 more comment
up vote
5
down vote
favorite
Here's my implementation of State Monad in TypeScript, based on a canonical Haskell implementation.
I would like it to get code reviewed.
class StateMonad<S, A>
constructor(public runState: (s: S) => ( s: S, a: A ))
static return_<S, A>(a: A): StateMonad<S, A>
return new StateMonad(s => ( s, a ));
bind<B>(func: (a: A) => StateMonad<S, B>): StateMonad<S, B>
return new StateMonad<S, B>((s: S) =>
const s: s_, a = this.runState(s);
return func(a).runState(s_);
);
// aux monad factory
const createCounter = (regex: RegExp) => new StateMonad((s: string) =>
s.split('')
.reduce((acc, c) =>
(regex.test(c)) ? s: acc.s.replace(c, ''), a: acc.a + 1 : acc,
s, a: 0 )
);
const countLowerCase = createCounter(/[a-z]/);
const countDigits = createCounter(/[0-9]/);
// usage example
const a = countLowerCase /* -- haskell equivalent */
.bind(n1 => countDigits /* do n1 <- countLowerCase */
.bind(n2 => StateMonad /* n2 <- countDigits */
.return_(n1 + n2))) /* return n1 + n2 */
.runState("abc123ABC");
typescript monads state
I am not big on functional styles but as I understand it the state object should be a new copy rather than just the reference moved to a new object? Or is the responsibility of isolating the state in the function usingStateMonad
? If so why would you need it?
â Blindman67
Apr 7 at 12:57
@Blindman67 I added a usage example, of how its intended to be used. Not sure what you mean new copy/reference moved. State gets "mutated" as it gets passed through a chain of monads.
â dark_ruby
Apr 7 at 16:31
I wonder how does this line work:const s: s_, a = this.runState(s);
. Object decomposition seems to be broken in parts: _s
. Where does the_s
come from? Is it a bug or I am missing something?
â Igor Soloydenko
Apr 11 at 19:23
@IgorSoloydenko no, it's not a bug, it's a feature :) you can rename decomposed variable to a different name, I'm doing it becauses
is already in scope. In fact you could have copied this code typescript online compiler and seen what it compiles to.
â dark_ruby
Apr 11 at 19:53
1
I tried writing a review, but really don't see anything to complain about besidesreturn_
being ugly for a public interface (just usereturn
) andcreateCounter
being unnecessarily complex (just make the regular expression global anda
will be the difference in lengths of the resulting string and original string)
â Gerrit0
Apr 12 at 22:38
 |Â
show 1 more comment
up vote
5
down vote
favorite
up vote
5
down vote
favorite
Here's my implementation of State Monad in TypeScript, based on a canonical Haskell implementation.
I would like it to get code reviewed.
class StateMonad<S, A>
constructor(public runState: (s: S) => ( s: S, a: A ))
static return_<S, A>(a: A): StateMonad<S, A>
return new StateMonad(s => ( s, a ));
bind<B>(func: (a: A) => StateMonad<S, B>): StateMonad<S, B>
return new StateMonad<S, B>((s: S) =>
const s: s_, a = this.runState(s);
return func(a).runState(s_);
);
// aux monad factory
const createCounter = (regex: RegExp) => new StateMonad((s: string) =>
s.split('')
.reduce((acc, c) =>
(regex.test(c)) ? s: acc.s.replace(c, ''), a: acc.a + 1 : acc,
s, a: 0 )
);
const countLowerCase = createCounter(/[a-z]/);
const countDigits = createCounter(/[0-9]/);
// usage example
const a = countLowerCase /* -- haskell equivalent */
.bind(n1 => countDigits /* do n1 <- countLowerCase */
.bind(n2 => StateMonad /* n2 <- countDigits */
.return_(n1 + n2))) /* return n1 + n2 */
.runState("abc123ABC");
typescript monads state
Here's my implementation of State Monad in TypeScript, based on a canonical Haskell implementation.
I would like it to get code reviewed.
class StateMonad<S, A>
constructor(public runState: (s: S) => ( s: S, a: A ))
static return_<S, A>(a: A): StateMonad<S, A>
return new StateMonad(s => ( s, a ));
bind<B>(func: (a: A) => StateMonad<S, B>): StateMonad<S, B>
return new StateMonad<S, B>((s: S) =>
const s: s_, a = this.runState(s);
return func(a).runState(s_);
);
// aux monad factory
const createCounter = (regex: RegExp) => new StateMonad((s: string) =>
s.split('')
.reduce((acc, c) =>
(regex.test(c)) ? s: acc.s.replace(c, ''), a: acc.a + 1 : acc,
s, a: 0 )
);
const countLowerCase = createCounter(/[a-z]/);
const countDigits = createCounter(/[0-9]/);
// usage example
const a = countLowerCase /* -- haskell equivalent */
.bind(n1 => countDigits /* do n1 <- countLowerCase */
.bind(n2 => StateMonad /* n2 <- countDigits */
.return_(n1 + n2))) /* return n1 + n2 */
.runState("abc123ABC");
typescript monads state
edited Apr 7 at 18:11
200_success
123k14142399
123k14142399
asked Apr 5 at 19:55
dark_ruby
1435
1435
I am not big on functional styles but as I understand it the state object should be a new copy rather than just the reference moved to a new object? Or is the responsibility of isolating the state in the function usingStateMonad
? If so why would you need it?
â Blindman67
Apr 7 at 12:57
@Blindman67 I added a usage example, of how its intended to be used. Not sure what you mean new copy/reference moved. State gets "mutated" as it gets passed through a chain of monads.
â dark_ruby
Apr 7 at 16:31
I wonder how does this line work:const s: s_, a = this.runState(s);
. Object decomposition seems to be broken in parts: _s
. Where does the_s
come from? Is it a bug or I am missing something?
â Igor Soloydenko
Apr 11 at 19:23
@IgorSoloydenko no, it's not a bug, it's a feature :) you can rename decomposed variable to a different name, I'm doing it becauses
is already in scope. In fact you could have copied this code typescript online compiler and seen what it compiles to.
â dark_ruby
Apr 11 at 19:53
1
I tried writing a review, but really don't see anything to complain about besidesreturn_
being ugly for a public interface (just usereturn
) andcreateCounter
being unnecessarily complex (just make the regular expression global anda
will be the difference in lengths of the resulting string and original string)
â Gerrit0
Apr 12 at 22:38
 |Â
show 1 more comment
I am not big on functional styles but as I understand it the state object should be a new copy rather than just the reference moved to a new object? Or is the responsibility of isolating the state in the function usingStateMonad
? If so why would you need it?
â Blindman67
Apr 7 at 12:57
@Blindman67 I added a usage example, of how its intended to be used. Not sure what you mean new copy/reference moved. State gets "mutated" as it gets passed through a chain of monads.
â dark_ruby
Apr 7 at 16:31
I wonder how does this line work:const s: s_, a = this.runState(s);
. Object decomposition seems to be broken in parts: _s
. Where does the_s
come from? Is it a bug or I am missing something?
â Igor Soloydenko
Apr 11 at 19:23
@IgorSoloydenko no, it's not a bug, it's a feature :) you can rename decomposed variable to a different name, I'm doing it becauses
is already in scope. In fact you could have copied this code typescript online compiler and seen what it compiles to.
â dark_ruby
Apr 11 at 19:53
1
I tried writing a review, but really don't see anything to complain about besidesreturn_
being ugly for a public interface (just usereturn
) andcreateCounter
being unnecessarily complex (just make the regular expression global anda
will be the difference in lengths of the resulting string and original string)
â Gerrit0
Apr 12 at 22:38
I am not big on functional styles but as I understand it the state object should be a new copy rather than just the reference moved to a new object? Or is the responsibility of isolating the state in the function using
StateMonad
? If so why would you need it?â Blindman67
Apr 7 at 12:57
I am not big on functional styles but as I understand it the state object should be a new copy rather than just the reference moved to a new object? Or is the responsibility of isolating the state in the function using
StateMonad
? If so why would you need it?â Blindman67
Apr 7 at 12:57
@Blindman67 I added a usage example, of how its intended to be used. Not sure what you mean new copy/reference moved. State gets "mutated" as it gets passed through a chain of monads.
â dark_ruby
Apr 7 at 16:31
@Blindman67 I added a usage example, of how its intended to be used. Not sure what you mean new copy/reference moved. State gets "mutated" as it gets passed through a chain of monads.
â dark_ruby
Apr 7 at 16:31
I wonder how does this line work:
const s: s_, a = this.runState(s);
. Object decomposition seems to be broken in part s: _s
. Where does the _s
come from? Is it a bug or I am missing something?â Igor Soloydenko
Apr 11 at 19:23
I wonder how does this line work:
const s: s_, a = this.runState(s);
. Object decomposition seems to be broken in part s: _s
. Where does the _s
come from? Is it a bug or I am missing something?â Igor Soloydenko
Apr 11 at 19:23
@IgorSoloydenko no, it's not a bug, it's a feature :) you can rename decomposed variable to a different name, I'm doing it because
s
is already in scope. In fact you could have copied this code typescript online compiler and seen what it compiles to.â dark_ruby
Apr 11 at 19:53
@IgorSoloydenko no, it's not a bug, it's a feature :) you can rename decomposed variable to a different name, I'm doing it because
s
is already in scope. In fact you could have copied this code typescript online compiler and seen what it compiles to.â dark_ruby
Apr 11 at 19:53
1
1
I tried writing a review, but really don't see anything to complain about besides
return_
being ugly for a public interface (just use return
) and createCounter
being unnecessarily complex (just make the regular expression global and a
will be the difference in lengths of the resulting string and original string)â Gerrit0
Apr 12 at 22:38
I tried writing a review, but really don't see anything to complain about besides
return_
being ugly for a public interface (just use return
) and createCounter
being unnecessarily complex (just make the regular expression global and a
will be the difference in lengths of the resulting string and original string)â Gerrit0
Apr 12 at 22:38
 |Â
show 1 more comment
1 Answer
1
active
oldest
votes
up vote
0
down vote
I'm probably off my rocker here, but what if rather than nested chains of bind, you used a fork/join type of approach?
static combine<T, S, R>(
monads: [P in keyof T]: StateMonad<S, T[P]> ,
selector: (values: T) => R
)
: StateMonad<S, R>
return new StateMonad<S, R>((state) =>
var ret: any = ;
for (const key in monads)
if (monads.hasOwnProperty(key))
ret[key] = monads[key].runState(state).a;
return a: selector(ret), s: state
);
and
var a = StateMonad
.combine(
countLowerCase,
countDigits
,
combined =>
combined.countLowerCase +
combined.countDigits
)
.runState("abc123ABC");
Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
â Dannnno
Jun 21 at 2:26
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
I'm probably off my rocker here, but what if rather than nested chains of bind, you used a fork/join type of approach?
static combine<T, S, R>(
monads: [P in keyof T]: StateMonad<S, T[P]> ,
selector: (values: T) => R
)
: StateMonad<S, R>
return new StateMonad<S, R>((state) =>
var ret: any = ;
for (const key in monads)
if (monads.hasOwnProperty(key))
ret[key] = monads[key].runState(state).a;
return a: selector(ret), s: state
);
and
var a = StateMonad
.combine(
countLowerCase,
countDigits
,
combined =>
combined.countLowerCase +
combined.countDigits
)
.runState("abc123ABC");
Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
â Dannnno
Jun 21 at 2:26
add a comment |Â
up vote
0
down vote
I'm probably off my rocker here, but what if rather than nested chains of bind, you used a fork/join type of approach?
static combine<T, S, R>(
monads: [P in keyof T]: StateMonad<S, T[P]> ,
selector: (values: T) => R
)
: StateMonad<S, R>
return new StateMonad<S, R>((state) =>
var ret: any = ;
for (const key in monads)
if (monads.hasOwnProperty(key))
ret[key] = monads[key].runState(state).a;
return a: selector(ret), s: state
);
and
var a = StateMonad
.combine(
countLowerCase,
countDigits
,
combined =>
combined.countLowerCase +
combined.countDigits
)
.runState("abc123ABC");
Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
â Dannnno
Jun 21 at 2:26
add a comment |Â
up vote
0
down vote
up vote
0
down vote
I'm probably off my rocker here, but what if rather than nested chains of bind, you used a fork/join type of approach?
static combine<T, S, R>(
monads: [P in keyof T]: StateMonad<S, T[P]> ,
selector: (values: T) => R
)
: StateMonad<S, R>
return new StateMonad<S, R>((state) =>
var ret: any = ;
for (const key in monads)
if (monads.hasOwnProperty(key))
ret[key] = monads[key].runState(state).a;
return a: selector(ret), s: state
);
and
var a = StateMonad
.combine(
countLowerCase,
countDigits
,
combined =>
combined.countLowerCase +
combined.countDigits
)
.runState("abc123ABC");
I'm probably off my rocker here, but what if rather than nested chains of bind, you used a fork/join type of approach?
static combine<T, S, R>(
monads: [P in keyof T]: StateMonad<S, T[P]> ,
selector: (values: T) => R
)
: StateMonad<S, R>
return new StateMonad<S, R>((state) =>
var ret: any = ;
for (const key in monads)
if (monads.hasOwnProperty(key))
ret[key] = monads[key].runState(state).a;
return a: selector(ret), s: state
);
and
var a = StateMonad
.combine(
countLowerCase,
countDigits
,
combined =>
combined.countLowerCase +
combined.countDigits
)
.runState("abc123ABC");
answered Jun 20 at 20:09
cwharris
1011
1011
Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
â Dannnno
Jun 21 at 2:26
add a comment |Â
Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
â Dannnno
Jun 21 at 2:26
Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
â Dannnno
Jun 21 at 2:26
Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
â Dannnno
Jun 21 at 2:26
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191350%2fstate-monad-in-typescript%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
I am not big on functional styles but as I understand it the state object should be a new copy rather than just the reference moved to a new object? Or is the responsibility of isolating the state in the function using
StateMonad
? If so why would you need it?â Blindman67
Apr 7 at 12:57
@Blindman67 I added a usage example, of how its intended to be used. Not sure what you mean new copy/reference moved. State gets "mutated" as it gets passed through a chain of monads.
â dark_ruby
Apr 7 at 16:31
I wonder how does this line work:
const s: s_, a = this.runState(s);
. Object decomposition seems to be broken in parts: _s
. Where does the_s
come from? Is it a bug or I am missing something?â Igor Soloydenko
Apr 11 at 19:23
@IgorSoloydenko no, it's not a bug, it's a feature :) you can rename decomposed variable to a different name, I'm doing it because
s
is already in scope. In fact you could have copied this code typescript online compiler and seen what it compiles to.â dark_ruby
Apr 11 at 19:53
1
I tried writing a review, but really don't see anything to complain about besides
return_
being ugly for a public interface (just usereturn
) andcreateCounter
being unnecessarily complex (just make the regular expression global anda
will be the difference in lengths of the resulting string and original string)â Gerrit0
Apr 12 at 22:38