[Functional Programming] Introduction to State, thinking in State

Recently, I am learning Working with ADT.

Got some extra thought about State Monad. Basiclly how to thinking in State.

First, we need to know the type of State: State returns Pair with Unit on the left, and state on the right:

State(state => Pair(Unit, state))

We don't need to manully add Pari and Unit type, State provides helper methods for doing this, but it is important to understand.

On the left side, Unit is a variable, types can be very depends on your needs. 

On the right side, state has to be a fixed type, you better do NOT change it.

 

To access right side 'state', we can using 'modify' or 'set' helpers.

To access left side 'Unit', we can using 'get' helper.

 

One benifit by using State, is that we got lazyness for the function, means we can chain different state transition together.

Let's see an example:

复制代码
const State = require('crocks/State');

const {get, modify} = State;

// add :: Int -> Int -> Int
const add = x => y => y + x;
// if we want to chain state transform, we need to have a function
// addNickel :: () -> State Int ()
//const addNickel = () => State(s => Pair(Unit(), s + 5))
const addNickel = () => modify(add(5));
// addDime = () -> State Int ()
const addDime = () => modify(add(10));
const addQuarter = () => modify(add(25));
复制代码

In the example, we define three 'addXX' functions, each add different values. 

We can compose them together:

复制代码
// state :: State Int()
const state = addNickel()
    .chain(addDime) // Pair( (), 15 )
    .chain(addQuarter) // Pair( (), 40 )
    .chain(addQuarter)  // Pair( (), 65 )
    .chain(addQuarter)  // Pair( (), 90 )
    .chain(addQuarter) // Pair( (), 115 )

console.log(
    state
    .runWith(0)
)
复制代码

It is important to call 'runWIth', 'execWith' or 'evalWith'... because State is lazy, you need to trigger it. We chain multi state together to get new state, or let's saying we are keep modfiying the state. At this point, we didn't touch the 'Unit' part.

 

Then why 'Unit' / left side part can be useful?

We can think 'Unit' / left side part is the result of 'state' / right side part after mapping to some logic / function.

For example, we want to build a function, only return True of False, if number is greater than 100, return True, otherwise return False:

复制代码
// canVend :: Int -> Boolean
const canVend = n => n >= 100;

console.log(
    get()
   .map(canVend)
    .runWith(0)
) // False

console.log(
    get()
   .map(canVend)
    .runWith(200)
) // True
复制代码

For calling 'get()', we are targeting left side part, which is 'Unit', it waiting some mapping function, which can transform state and put result into Unit. If we don't provide any mapping function, 'get()' will just copy the value from 'state':

复制代码
console.log(
    get()
    .runWith(10)
) // Pair(10, 10)

console.log(
    get()
     .map(x => x * 2)
    .runWith(10)
) // Pair(20, 10)

// the same as:
console.log(
    get(x => x * 2)
    .runWith(10)
) // Pair(20, 10)
复制代码

 

In 'addNickle' example, we want to only get result in Boolean, if the state is greater than 100 or not, we can keep the state transform part untouched, only chain the getter logic in final state.

复制代码
// canVend :: Int -> Boolean
const canVend = n => n >= 100;

// evaluate :: () -> State Int Bool
const evaluate = () => get(canVend); // get().map(fn) === get(fn)

// state :: State Int()
const state = addNickel()
    .chain(addDime) // Pair( (), 15 )
    .chain(addQuarter) // Pair( (), 40 )
    .chain(addQuarter)  // Pair( (), 65 )
    .chain(addQuarter)  // Pair( (), 90 )
    .chain(addQuarter) // Pair( (), 115 )

console.log(
    state
    .chain(evaluate)// Pair( true, 115 )
    .runWith(0)
)
复制代码

 

Full Code:

---

复制代码
const State = require('crocks/State');

const {get, modify} = State;

// add :: Int -> Int -> Int
const add = x => y => y + x;// if we want to chain state transform, we need to have a function
// addNickel :: () -> State Int ()
//const addNickel = () => State(s => Pair(Unit(), s + 5))
const addNickel = () => modify(add(5));
// addDime = () -> State Int ()
const addDime = () => modify(add(10));
const addQuarter = () => modify(add(25));

// canVend :: Int -> Boolean
const canVend = n => n >= 100;

// evaluate :: () -> State Int Bool
const evaluate = () => get(canVend); // get().map(fn) === get(fn)

// state :: State Int()
const state = addNickel()
    .chain(addDime) // Pair( (), 15 )
    .chain(addQuarter) // Pair( (), 40 )
    .chain(addQuarter)  // Pair( (), 65 )
    .chain(addQuarter)  // Pair( (), 90 )
    .chain(addQuarter) // Pair( (), 115 )

console.log(
    state
    .chain(evaluate)// Pair( true, 115 )
    .runWith(0)
)
复制代码

 

posted @   Zhentiw  阅读(259)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2016-03-25 [Angular 2] Simple intro Http
2016-03-25 [Typescript] Typescript Enums vs Booleans when Handling State
2015-03-25 [React] React Fundamentals: Mixins
2015-03-25 [React] React Fundamentals: Component Lifecycle - Updating
点击右上角即可分享
微信分享提示