data List a = Nil | Cons a (List a)
interface Nil { type: 'nil' } interface Cons<T> { type: 'cons' head: T tail: List<T> } type List<T> = Nil | Cons<T>
We're going to solve that problem by using this incredbly powerful design strategy that you already seen us use over and over.
And that's the strategy of wishful thinking.
— Harold Abelson
export type Rat = [number, number] export declare makeRat = (numer, denom) => Rat export declare numer = (rat: Rat) => number export declare denom = (rat: Rat) => number
export interface ToDoItem { id: string title: string done: boolean }
export const create = (id: string, title: string, done: boolean): ToDoItem => ({ id, title, done }) export const id = (item: ToDoItem): string => item.id export const title = (item: ToDoItem): string => item.title export const done = (item: ToDoItem): boolean => item.done export const update = (done: boolean) => (item: ToDoItem) => ({ id: item.id, title: item.title, done, })
export type ToDoItem = [string, string, boolean] export const create = (id: string, title: string, done: boolean): ToDoItem => [id, title, done] export const id = ([id]: ToDoItem): string => id export const title = ([, title]: ToDoItem): string => title export const done = ([,, done]: ToDoItem): boolean => done export const update = (done: boolean) => ([id, title]: ToDoItem) => [id, title, done]
import * as TDI from './ToDoItem' export interface ToDoList { key: number todos: TDI.ToDoItem[] }
export const create = (): ToDoList => ({ key: 0, todos: [] }) export const append = (title: string) => (list: ToDoList): ToDoList => { const { key: prev, todos } = list const key = prev + 1 const item = TDI.create(`${key}`, title, false) return { key, todos: [...todos, item] } }
export const updateById = (id: string, done: boolean) => (list: ToDoList): ToDoList => { const { key, todos } = list const i = todos.findIndex((item) => item.id === id) if (i === -1) return list return { key, todos: [ ...todos.slice(0, i), TDI.update(done)(todos[i]), ...todos.slice(i + 1) ] } }
export const removeById = (id: string) => (list: ToDoList): ToDoList => { const { key, todos } = list const i = todos.findIndex((item) => item.id === id) if (i === -1) return list return { key, todos: [ ...todos.slice(0, i), ...todos.slice(i + 1) ] } }
import * as TDL from './ToDoList' export default class ToDoList { private _toDoList: TDL.ToDoList constructor() { this._toDoList = TDL.create() } append(title: string) { this._toDoList = TDL.append(title)(this._toDoList) } updateById(id: string, done: boolean) { this._toDoList = TDL.updateById(id, done)(this._toDoList) } }
Promise.resolve(toDoList) .then(TDL.append('foobar')) .then(TDL.updateById('0', true))
const Comp = () => { const [toDoList, setToDoList] = useState(TDL.create()) const handleToDoUpdate = (id: string, done: boolean) => { setToDoList(TDL.updateById(id, done)) } }
|>
operatortoDoList |> TDL.append('foobar') |> TDL.updateById('0', true)
toDoList |> TDL.append('foobar')(%) |> TDL.updateById('0', true)(%)
Both Hack pipes and F# pipes respectively impose a small syntax tax on different expressions:
Hack pipes slightly tax only unary function calls, and
F# pipes slightly tax all expressions except unary function calls.
But with F# pipes,
value |> someFunction + 1
is still valid syntax – it’ll just fail late at runtime, becausesomeFunction + 1
isn’t callable
讀一下 tc39/proposal-pipeline-operator ,看看你喜歡哪一種?
interface Action<T> { type: 'redux/someAction' payload: T }
interface RequestAsyncAction<C> { type: 'async/request' context: C } interface SuccessAsyncAction<C, T> { type: 'async/success' context: C payload: T } interface FailureAsyncAction<C> { type: 'async/failure' context: C error: Error } export type AsyncAction<C, T> = | RequestAsyncAction<C> | SuccessAsyncAction<C, T> | FailureAsyncAction<C>
export const request = <C>(context: C): RequestAsyncAction<C> => ({ type: 'async/request', context, }) export const success = <C, T>(context: C, payload: T): SuccessAsyncAction<C, T> => ({ type: 'async/success', context, payload }) export const failure = <C>(context: C, error: Error): FailureAsyncAction<C> => ({ type: 'async/failure', context, error })
export const append = (title: string) => async (dispatch: AppendDispatch) => { dispatch(AsyncAction.request(title)) try { const item = await ToDoService.create(title) dispatch(AsyncAction.success(title, ToDoListAction.append(item))) } catch (err: any) { dispatch(AsyncAction.failure(title, err)) } }
const reducer = (state = initialState, action: Action): State => { switch (action.type) { case 'async/request': { return { ...state, isLoading: true, } } case 'async/success': { const act = action.payload as ToDoListAction.Action return { isLoading: false, list: toDoListReducer(state.list, act) } } case 'async/failure': { return { ...state, isLoading: false, } } default: return state } }
const reducer = (state = initialState, action: Action): TDL.ToDoList => { switch (action.type) { case 'todos/append': { return TDL.append(action.payload)(state) } case 'todos/updateById': { const [id, item] = action.payload return TDL.updateById(id, item)(state) } case 'todos/removeById': { return TDL.removeById(action.payload)(state) } default: return state } }
"some/action/pending"
"some/action/fulfilled"
"some/action/rejected"
function add(a: number, b: number): number { return a + b } console.log(add(1, 2))
function add(a: number, b: number, cb: (x: number) => void): void { cb(a + b) } add(1, 2, (c) => console.log(c))
function add(a: number, b: number): Promise<number> { return Promise.resolve(a + b) } add(1, 2).then(console.log)
const c = await add(1, 2) console.log(c)
const Comp = () => { const p = useMemo(() => add(1, 2), []) const c = usePromise(p) return ( <span>1 + 2 = {c}</span> ) }
import { useState, useMemo, useEffect } from 'react'; import { BehaviorSubject } from 'rxjs'; export default function useObservable(input$, initialState) { const [state, setState] = useState(initialState); const state$ = useMemo(() => new BehaviorSubject(initialState), []); useEffect(() => { const sub = state$.subscribe(x => setState(x)); return sub.unsubscribe.bind(sub); }, [state$]); useEffect(() => { const sub = input$.subscribe(state$.next.bind(state$)); return sub.unsubscribe.bind(sub); }, [input$]); return state; }
import { createComponent } from '@vue/composition-api' import { usePromise } from 'vue-compose-promise' export default createComponent({ setup() { const promised = usePromise({ pendingDelay: 200, promise: fetchUsers() }) return { usersPromise: promised.state, fetchUsers() { promised.state.promise = fetchUsers() }, } }, })
import { useState, useEffect } from 'react'; export default function useArray(xs) { const [s, set] = useState(xs[0]); useEffect(() => { for (let v of xs) { setImmediate(set, v); } }, [xs]); return s; }
export type LazyPromise<T> = () => Promise<T> export const of = <T>(x: T) => () => Promise.resolve(x) export const then = <T, U>(f: (x: T) => LazyPromise<U>) => (lp: LazyPromise<T>): LazyPromise<U> => () => lp().then((x: T) => f(x)())
import * as LP from './LazyPromise' const one = LP.of(1) const two = LP.of(2) describe('LazyPromise', () => { it('should chain lazy promises', async () => { const run = one |> LP.then((x) => two |> LP.then((y) => LP.of(x + y) )) expect(await run()).toBe(3) }) })