[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) )
相关文章
- Your password has expired. To log in you must change it using a client that supports expired passwords.
- There is insufficient system memory to run this query 错误
- [React Recoil] Write a Custom Recoil Hook to Reset a Value in the Recoil State
- [Tools] Using z to jump to "frecent" folders - Command Line Power User
- [CSS] Conditionally Assign Style to a Parent Element with Focus-Within Pseudo-class
- [Svelte 3] Use an onMount lifecycle method to fetch and render data in Svelte 3
- [WASM Rust] Use the js-sys Crate to Invoke Global APIs Available in Any JavaScript Environment
- [Angular] How to get Store state in ngrx Effect
- [Typescript] Introduction to Generics in Typescript
- [Angular 2] Refactoring mutations to enforce immutable data in Angular 2
- How to use udev for Oracle ASM in Oracle Linux 6 怎样使用udev在linux 6系统上使用asm
- [Tools] Make a zsh function alias to enforce using yarn commands when in a yarn project
- [Svelte 3] Use await block to wait for a promise and handle loading state in Svelte 3
- [HTML5] Add an SVG Image to a Webpage and Get a Reference to the Internal Elements in JavaScript
- [React] Pass a function to setState in React
- [React] Call setState with null to Avoid Triggering an Update in React 16
- [Nuxt] Add Arrays of Data to the Vuex Store and Display Them in Vue.js Templates
- Try to create new xs project in AG3
- Custom tool error: Failed to generate code for the service reference ××××××. Please check other erro
- Cannot add product to Opportunity in Fiori - RFC error
- vuex报错:‘xxx’was assigned to but it has no setter.
- 在Android Studio上导入别人的项目后报错提示:......drawable-hdpixxx.9.png: AAPT: error: file failed to compile.
- 成功解决TypeError: Encoders require their input to be uniformly strings or numbers. Got [‘float‘, ‘int‘,
- 【已解决杀80端口】nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
- 已解决selenium.common.exceptions.WebDriverException: Messag: ‘geckodriver‘ executable needs to be in PA
- error[No partition metadata for topic test-1 due to kafka.common.LeaderNotAvailableException]
- How to gathering Database Statistics in Oracle
- Introduction to Command Line
- 解决运行js代码报错—Warning: To load an ES module, set “type“: “module“ in the package.json or use the .mjs
- 【异常】MyBatis提示TooManyResultsException: Expected one result (or null) to be returned by selectOne()
- Linux Tips: How to Create Link in Linux?