注入具有副作用的功能 [英] Inject functions with side-effects
问题描述
使用高阶函数时遇到问题。
比方说,我有下面的代码不使用它们(而是调用全局函数):
$ $ p $ import { db_insert} from'some-db-lib'//带副作用的函数
const save_item =(item)=> {
//一些逻辑,如验证项目数据...
db_insert(item)//调用全局的db_insert
}
$ b $常量handle_request =(请求)=> ; {
//一些逻辑如清理请求...
save_item(request.data)//全局调用save_item
}
handle_request(some_request)
现在,通过使用高阶函数(注入副作用作为函数参数) / p>
从'some-db-lib'导入{db_insert} //带副作用的函数
const save_item =(item,insert)=> {//注入插入
//一些逻辑,如验证项目数据...
insert(item)
}
const handle_request =(request,save,insert )=> {//注入save并插入
//一些逻辑,如清理请求...
save(request.data,insert)
}
handle_request(some_request, save_item,db_insert)
想象一下,使用更大的函数树互相调用。最后一个例子会变成一大堆函数将函数传递给对方。
这是隔离副作用的正确方法吗?我错过了什么?
订单功能。假设我有下面的代码不使用它们(而是调用全局函数):
c = (x)=> console.log(x)
b =(x)=> c(x)
a =(x)=> b(x)
a('Hello world')
但这是一个可怕的起点,说实话
-
c
只是console.log的 eta转换
-
b
只是c $ c $的eta转换
-
a
只是b
换句话说, 常见示例: 人们喜欢使用 这里发生了什么?它实际上很整齐。我们可以使用一个输入数组 大图 好的,所以这是一个在JavaScript中使用高阶函数的非常实用的例子,但是... 我缺少什么? 是的。高阶函数具有非常深刻而有意义的力量。让我提出另一组问题: 如果我告诉你只有函数需要完成所有操作,那该怎么办? 我仔细看看,你会看到这段代码并没有使用任何原生数据结构JavaScript(除了 它是如何做到的?数据在哪里? 总之,数据存在于部分应用函数中。让我们专注于一个特定的代码片段,让我可以告诉你我的意思是什么 当我们创建 a
=== b code> ===
仔细查看数据如何存储在 c
=== console.log
- 如果您要了解更高您需要一个更好的起点
map
Array.prototype.map
const f = x => x + 1const g = x => x * 2const xs = [1,2,3] console.log(xs.map(f))// [2,3,4] console.log(xs.map(g))// [2,4, 6]
xs
并创建一个新的数组,其中每个元素都是 xs
使用高阶函数。高阶函数可以是一次性使用 lambda ,或者它可以是 函数,它已经在其他地方定义过。
// xs.map(f)
[f( 1 ),f( 2 ),f(3 b)]
[2],[b] 3 [/ b],[b] 4 [/ b]]
// xs.map(g)
[g( 1 ),g( 2 ),g( 3 >)]
[ 2 , 4 , 6 ]
// xs.map(x => x * x)
[(x => x * x)(1 ),(x => x * x)(2 ), x => x * x)( 3 )]
[ 1 ,<4> , 9
lockquote
map
over他们是吧?
// emptyconst empty = nullconst isEmpty = x => x === empty // pairconst cons =(x,y)=> f => f(x,y)const car = p => p((x,y)=> x)const cdr = p => p((x,y)=> y)// listconst list =(x,... xs)=> x === undefined? empty:cons(x,list(... xs))const map =(f,xs)=> isEmpty(xs)? empty:cons(f(car(xs)),map(f,cdr(xs)))const list2str =(xs,acc ='(')=> isEmpty(xs)?acc +')':list2str cdr(xs),acc + car(xs)+'')//通用functionsconst f = x => x + 1const g = x => x * 2 //您的dataconst数据= list(1,2,3)console.log(list2str(map(f,data)))//'(2 3 4)'console.log(list2str(map(g,数据)))//'(2 4 6)'console.log(list2str(map(x => x * x,data)))//'(1 4 9)'
数字
为例子数据和字符串
用于输出内容以外)。没有 Object
s,没有 Array
s。没有技巧。只需功能
s。
const cons =(x,y)=> f => f(x,y)const car = p => p((x,y)=> x)const cdr = p => p((x,y)=> y)const pair = cons(1,2)console.log(car(pair))// 1console.log(cdr(pair))// 2
pair
code> cons(1,2) pair
中。它是什么形式? cons
返回一个函数,它绑定到 x
和 y
值 1
和 2
。这个函数,我们称之为 p
,正在等待被另一个函数 f
调用,它将应用 f
至 x
和 y
。 car
和 cdr
提供该函数( f
)并返回在 car
, x
的情况下选择期望的值。在 cdr
的情况下,选择 y
。
让我们重复...
我错过了什么吗?
是的。您刚刚目睹了多功能数据结构的起源,除了(高阶)函数之外。
您的语言是否缺少您可能需要的特定数据结构?你的语言是否提供一流的功能?如果你对这两者都回答'是',那么没有任何问题,因为你可以只使用函数来实现你需要的特定数据结构。
/ em>是高阶函数的力量。 不情愿的说服 好的,也许你认为我在那里做了一些技巧,替换上面的 下面是我们将为一个新的字典类型 下面,我将保持不使用除函数之外的任何东西(以及用于演示输出的字符串目的),但是这次我将实现一个可以保存键/值对的不同数据结构。您可以读取值,写入新值,并根据键覆盖现有值。 这将强化高阶数据的概念 ,即数据是抽象的抽象 - 即,使用 当然现在你看到高阶函数的力量,对吗? ^ _ ^ I'm having an issue when using higher-order functions.
Let's say I have the following code that doesn't use them (instead call global functions): And now, the same example, by using higher-order functions (inject side-effects as function arguments): Imagine this with a larger tree of functions calling each other. The last example would become a big mess of functions passing functions down to each other. Is this the correct way to isolate side-effects? Am I missing something? I'm having an issue when using higher-order functions. Let's say I have the following code that doesn't use them (instead call global functions):
But this is a terrible starting point, to be honest In other words, The common example: People love to demonstrate higher-order functions using
What's happening here? It's actually pretty neat. We can take an input array
The bigger picture OK, so that's a very practical example of using higher-order functions in JavaScript, but ... Am I missing something? Yes. Higher-order functions have an immensely deep and meaningful power. Let me pose another set of questions: What if I told you only functions were required to do all of it?
I you look closely, you'll see this code doesn't use any native data structures provided by JavaScript (with the exception of How does it do that? Where is the data? In short, the data exists in partially applied functions. Let's focus on one specific piece of code so I can show you what I mean
When we create So let's repeat... "Am I missing something?" Yes. What you just witnessed with the genesis of a versatile data structure out of nothing but (higher-order) functions. Is your language missing a particular data structure you might need? Does your language offer first class functions? If you answered 'yes' to both of those, there is no problem because you can materialize the particular data structure you need using nothing but functions. That is the power of higher-order functions. Reluctantly convinced OK, so maybe you're thinking I pulled some tricks there making a replacement for Here's the API we will make for a new dictionary type, Below, I will keep the same promise of not using anything except functions (and strings for demo output purposes), but this time I will implement a different data structure that can hold key/value pairs. You can read values, write new values, and overwrite existing value based on a key. This will reinforce the concept of higher-order data, that is data that is an abstraction of a lower abstraction – ie,
Surely now you see the power of higher-order functions, right ? ^_^ 这篇关于注入具有副作用的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
Array
。我向你保证,这里不涉及任何技巧。
dict
$ b
dict(key1,value1,key2,value2,...) - > d
read(d,key1) - > value1
read(d,key2) - > value2
write(d,key3,value3) - > d'
read(d',key3) - > value3
节点
来实现 list
这是使用 cons
等实现的
// emptyconst empty = nullconst isEmpty = x => x === empty // pairconst cons =(x,y)=> f => f(x,y)const car = p => p((x,y)=> x)const cdr = p => p((x,y)=> y)// listconst list =(x,... xs)=> x === undefined? empty:cons(x,list(... xs))const cadr = p => car(cdr(p))const cddr = p => cdr(cdr(p))const caddr = p => car(cddr(p))const cadddr = p => cadr(cddr(p))// nodeconst node =(key,value,left = empty,right = empty)=> list(key,value,left,right)const key = carconst value = cadrconst left = caddrconst right = cadddr // dictconst dict =(k,v,... rest)=> v ===未定义? empty:write(dict(... rest),k,v)const read =(t = empty,k)=> isEmpty(t)?未定义:k <键(t)?读(left(t),k):k>键(t)?读(right(t),k):value(t)const write =(t = empty,k,v)=> isEmpty(t)?节点(k,v):k <键(t)?节点(key(t),value(t),write(left(t),k,v),right(t)):k>键(t)?节点(key(t),value(t),left(t),write(right(t),k,v)):节点(k,v,left(t),right(t))let d = dict ('a',1,'b',2)console.log(read(d,'a'))// 1console.log(read(d,'b'))// 2console.log(read(d ,'c'))// undefinedd = write(d,'c',3)console.log(read(d,'c'))// 3
import {db_insert} from 'some-db-lib' // function with side-effect
const save_item = (item) => {
// some logic like validating item data...
db_insert(item) // call db_insert globally
}
const handle_request = (request) => {
// some logic like sanitizing request...
save_item(request.data) // call save_item globally
}
handle_request(some_request)
import {db_insert} from 'some-db-lib' // function with side-effect
const save_item = (item, insert) => { // inject insert
// some logic like validating item data...
insert(item)
}
const handle_request = (request, save, insert) => { // inject save and insert
// some logic like sanitizing request...
save(request.data, insert)
}
handle_request(some_request, save_item, db_insert)
c = (x) => console.log(x)
b = (x) => c(x)
a = (x) => b(x)
a('Hello world')
c
is just an eta conversion of console.log
b
is just an eta conversion of c
a
is just an eta conversion of b
a
=== b
=== c
=== console.log
– If you're going to understand higher-order functions, you need a better starting point
map
Array.prototype.map
const f = x => x + 1
const g = x => x * 2
const xs = [1,2,3]
console.log (xs.map (f)) // [2,3,4]
console.log (xs.map (g)) // [2,4,6]
xs
and create a new array where each element is the transformation of an element in xs
using a higher-order function. The higher-order function could be a one-time use lambda, or it could be a named function that was already defined elsewhere.// xs.map(f)
[ f(1), f(2), f(3) ]
[ 2 , 3 , 4 ]
// xs.map(g)
[ g(1), g(2), g(3) ]
[ 2 , 4 , 6 ]
// xs.map(x => x * x)
[ (x => x * x)(1), (x => x * x)(2), (x => x * x)(3) ]
[ 1 , 4 , 9 ]
map
over them, right?// empty
const empty = null
const isEmpty = x => x === empty
// pair
const cons = (x,y) => f => f (x,y)
const car = p => p ((x,y) => x)
const cdr = p => p ((x,y) => y)
// list
const list = (x,...xs) =>
x === undefined ? empty : cons (x, list (...xs))
const map = (f,xs) =>
isEmpty (xs) ? empty : cons (f (car (xs)), map (f, cdr (xs)))
const list2str = (xs, acc = '( ') =>
isEmpty (xs) ? acc + ')' : list2str (cdr (xs), acc + car (xs) + ' ')
// generic functions
const f = x => x + 1
const g = x => x * 2
// your data
const data = list (1, 2, 3)
console.log (list2str (map (f, data))) // '( 2 3 4 )'
console.log (list2str (map (g, data))) // '( 2 4 6 )'
console.log (list2str (map (x => x * x, data))) // '( 1 4 9 )'
Number
for example data and String
for purposes of outputting something to see). No Object
s, no Array
s. No tricks. Just Function
s. const cons = (x,y) => f => f (x,y)
const car = p => p ((x,y) => x)
const cdr = p => p ((x,y) => y)
const pair = cons (1,2)
console.log (car (pair)) // 1
console.log (cdr (pair)) // 2
pair
using cons(1,2)
look carefully how the data gets stored in pair
. What form is it in? cons
returns a function with x
and y
bound to the values 1
and 2
. This function, we'll call it p
, is waiting to be called with another function f
, which will apply f
to x
and y
. car
and cdr
provide that function (f
) and return the desired value – in the case of car
, x
is selected. in the case of cdr
, y
is selected.
Array
above. I assure you, there are no tricks involved.dict
dict (key1, value1, key2, value2, ...) --> d
read (d, key1) --> value1
read (d, key2) --> value2
write (d, key3, value3) --> d'
read (d', key3) --> value3
dict
is implemented using node
which is implemented using list
which is implemented using cons
, etc// empty
const empty = null
const isEmpty = x => x === empty
// pair
const cons = (x,y) => f => f (x,y)
const car = p => p ((x,y) => x)
const cdr = p => p ((x,y) => y)
// list
const list = (x,...xs) =>
x === undefined ? empty : cons (x, list (...xs))
const cadr = p => car (cdr (p))
const cddr = p => cdr (cdr (p))
const caddr = p => car (cddr (p))
const cadddr = p => cadr (cddr (p))
// node
const node = (key, value, left = empty, right = empty) =>
list (key, value, left, right)
const key = car
const value = cadr
const left = caddr
const right = cadddr
// dict
const dict = (k,v,...rest) =>
v === undefined ? empty : write (dict (...rest), k, v)
const read = (t = empty, k) =>
isEmpty (t)
? undefined
: k < key (t)
? read (left (t), k)
: k > key (t)
? read (right (t), k)
: value (t)
const write = (t = empty, k, v) =>
isEmpty (t)
? node (k, v)
: k < key (t)
? node (key (t), value (t), write (left (t), k, v), right (t))
: k > key (t)
? node (key (t), value (t), left (t), write (right (t), k, v))
: node (k, v, left (t), right (t))
let d = dict ('a', 1, 'b', 2)
console.log (read (d, 'a')) // 1
console.log (read (d, 'b')) // 2
console.log (read (d, 'c')) // undefined
d = write (d, 'c', 3)
console.log (read (d, 'c')) // 3