成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Ramda 哪些讓人困惑的函數(shù)簽名規(guī)則

開(kāi)發(fā) 前端
在類(lèi)型簽名中F?是一個(gè)類(lèi)型構(gòu)造器, 既和Array一樣的 「返回類(lèi)型的類(lèi)型」, 然而, TypeScript 里根本無(wú)法聲明"一個(gè)類(lèi)型參數(shù)為類(lèi)型構(gòu)造器"。

在我們查閱 Ramda 的文檔 時(shí), 常會(huì)見(jiàn)到一些"奇怪"的類(lèi)型簽名和用法,例如:

(Applicative f, Traversable t) => (a → f a) → t (f a) → f (t a)

或者,某一些函數(shù)"奇怪"的用法:

// R.ap can also be used as S combinator // when only two functions are passed 
R.ap(R.concat, R.toUpper)('Ramda') //=> 'RamdaRAMDA'

這些"奇怪"的點(diǎn)背后投射著 Ramda "更深"一層的設(shè)計(jì)邏輯, 本文將會(huì)對(duì)此作出講解, 并闡述背后通用的函數(shù)式編程理論知識(shí)。

Ramda 為人熟知的一面?

Ramda 經(jīng)常被當(dāng)做 Lodash 的另外一個(gè)"更加FP"的替代庫(kù),相對(duì)于 Lodash,Ramda 的優(yōu)勢(shì)(之一)在于完備的柯里化與 data last 的設(shè)計(jì)帶來(lái)的便捷的管道式編程(pipe)。

舉一個(gè)簡(jiǎn)單的代碼對(duì)比示例:

  • Ramda:
const myFn = R.pipe (
R.fn1,
R.fn2 ('arg1', 'arg2'),
R.fn3 ('arg3'),
R.fn4
)
  • Lodash:
const myFn = (x, y) => {
const var1 = _.fn1 (x, y)
const var2 = _.fn2 (var1, 'arg1', 'arg2')
const var3 = _.fn3 (var2, 'arg3')
return _.fn4 (var3)
}

Ramda 類(lèi)型簽名?

在 Ramda 的 API 文檔中, 類(lèi)型簽名的語(yǔ)法有些"奇怪":

  • add: Number → Number → Number

我們結(jié)合 Ramda 的柯里化規(guī)則, 稍加推測(cè), 可以將這個(gè)函數(shù)轉(zhuǎn)換為T(mén)ypeScript 的定義:

export function add(a: number, b: number): number;
export function add(a: number): (b: number) => number;

OK, 那為什么Ramda 的文檔不直接使用TypeScript 表達(dá)函數(shù)的類(lèi)型呢? -- 因?yàn)楦雍?jiǎn)潔!

Ramda 文檔中的類(lèi)型簽名使用的是Haskell 的語(yǔ)法, Haskell 作為一門(mén)純函數(shù)式編程語(yǔ)言, 可以很簡(jiǎn)潔地表達(dá)柯里化的語(yǔ)義, 相較之下, TypeScript 的表達(dá)方式就顯得比較臃腫。

當(dāng)然, 使用Haskell 的類(lèi)型簽名的意義不僅于此, 讓我們?cè)倏纯雌渌?奇怪"的函數(shù)類(lèi)型:

  • ap:
[a → b][a][b]
Apply f => f (a → b) → f a → f b
(r → a → b)(r → a)(r → b)

結(jié)合文檔中的demo:

R.ap([R.multiply(2), R.add(3)], [1,2,3]); //=> [2, 4, 6, 4, 5, 6]

R.ap([R.concat('tasty '), R.toUpper], ['pizza', 'salad']); //=> ["tasty pizza", "tasty salad", "PIZZA", "SALAD"]

// R.ap can also be used as S combinator
// when only two functions are passed
R.ap(R.concat, R.toUpper)('Ramda') //=> 'RamdaRAMDA'

[a → b] → [a] → [b]我們好理解, 就是笛卡爾積;

(r → a → b) → (r → a) → (r → b)我們也能理解, 就是兩個(gè)函數(shù)的串聯(lián);

Apply f => f (a → b) → f a → f b就有點(diǎn)難理解了, 語(yǔ)法上就有些陌生, 我們先將其翻譯成TypeScript 語(yǔ)法:

:), 好吧, 這段類(lèi)型沒(méi)法簡(jiǎn)單地翻譯成TypeScript, 因?yàn)? TypeScript 不支持將 「類(lèi)型構(gòu)造器」 作為類(lèi)型參數(shù)!舉個(gè)例子:

type T<F> = F<number>;

報(bào)錯(cuò)信息如下:

Type 'F' is not generic.

在類(lèi)型簽名中F?是一個(gè)類(lèi)型構(gòu)造器, 既和Array一樣的 「返回類(lèi)型的類(lèi)型」, 然而, TypeScript 里根本無(wú)法聲明"一個(gè)類(lèi)型參數(shù)為類(lèi)型構(gòu)造器"。

正如示例中type T<F> = F<number>;?中, 我們無(wú)法告訴TypeScript, 這里的F?是一個(gè)類(lèi)型構(gòu)造器, 所以當(dāng)將number?傳入F的時(shí)候, 就報(bào)錯(cuò)了。

OK, 我們假設(shè)TypeScript 支持聲明"一個(gè)類(lèi)型參數(shù)為類(lèi)型構(gòu)造器", 讓我們?cè)賮?lái)看看Apply f => f (a → b) → f a → f b該怎么翻譯:

type AP = <F extends Appy, A, B>(f: F<((a: A) => B)>) => (fa: F<A>) => F<B>;

這里的F可以理解為一種 「上下文」, 這段類(lèi)型簽名可以先簡(jiǎn)單地理解為:

將一個(gè)包裹在上下文中的「函數(shù)」取出, 再將另一個(gè)包裹在上下文中的「值」取出, 調(diào)用函數(shù)后, 將函數(shù)的返回值重新包裹進(jìn)上下文中并返回。

這里的 「上下文」 是一個(gè)泛指, 比如我們可以將其特異化(specialize)為 Promise :

type AP = <A, B>(f: Promise<((a: A) => B)>) => (fa: Promise<A>) => Promise<B>;  
const ap: AP = (f) => fa => f.then(ff => fa.then(ff));

ap? 或說(shuō) Apply 作為函數(shù)式編程中的一種常見(jiàn)抽象, 有非常重要重要的學(xué)習(xí)意義, 但其抽象的解析超出本文范圍, 在這里我們只聚焦于「是什么」, 暫不考慮「為什么」。

那么, (r → a → b) → (r → a) → (r → b)與Apply f => f (a → b) → f a → f b是什么關(guān)系?

他們之間是同父異母的關(guān)系, (r → a → b) → (r → a) → (r → b)?是對(duì)Apply f => f (a → b) → f a → f b的特異化, 正如我們對(duì)Promise 做的那樣。

函數(shù)也可以是一個(gè) 「上下文」?

答案是可以的, 我們可以將一個(gè)一元函數(shù)a -> b?理解為"一個(gè)包裹在上下文中的b?, 只不過(guò)為了獲取這個(gè)b?, 需要先傳入一個(gè)a。

先看看 Haskell 對(duì)ap 的定義:

instance Applicative ((->) r) where
(<*>) f g x = f x (g x)

替換為T(mén)ypeScript 的實(shí)現(xiàn), 我們將上面的Promise 的例子稍微修改下, 得出:

type F<A> = (a: any) => A;

type AP = <A, B>(f: F<((a: A) => B)>) => (fa: F<A>) => F<B>;

const ap: AP = f => fa => {
return (r) => f(r)(fa(r));
}

同樣的, 我們得到Apply 特異化為Array 的實(shí)現(xiàn):

type AP = <A, B>(f: Array<((a: A) => B)>) => (fa: Array<A>) => Array<B>;

const ap: AP = f => fa => {
return f.flatMap(ff => fa.map(ff));
};

綜上所述, 我們可以得出結(jié)論:

ap的類(lèi)型簽名[a → b] → [a] → [b]和(r → a → b) → (r → a) → (r → b)是Apply f => f (a → b) → f a → f b的特異化。

責(zé)任編輯:武曉燕 來(lái)源: Tecvan
相關(guān)推薦

2012-04-25 10:18:49

jQuery

2021-08-26 15:44:33

路由函數(shù)ASP

2009-08-14 09:19:15

Windows 7XP模式優(yōu)缺點(diǎn)

2011-09-15 09:19:30

2020-01-14 18:20:48

OA選型CIO

2015-09-21 14:44:54

物聯(lián)網(wǎng)

2018-05-03 08:13:35

2015-02-11 15:40:40

XY蘋(píng)果助手iOS9

2023-11-28 12:19:49

C++函數(shù)指針

2021-01-07 08:05:20

JenkinsDevOps

2023-01-03 17:51:05

2015-06-08 09:18:53

軟件永生軟件規(guī)則

2025-03-10 00:17:00

2009-09-25 15:15:17

算法

2016-03-31 16:50:54

2021-03-26 06:36:50

安全軟件殺毒軟件免費(fèi)安全軟件

2022-03-04 06:46:30

Python代碼

2022-07-29 11:02:17

Web3NFT元宇宙

2022-03-04 09:43:18

UDP日志TCP

2015-08-25 08:55:14

優(yōu)秀代碼基因
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品夜夜春夜夜爽久久电影 | 一区二区三区免费网站 | 亚洲精品一区在线 | 欧美日韩国产一区二区三区 | 玖玖精品视频 | 久久久久国产精品 | 国产精品91视频 | 日韩国产一区二区 | 91久久精品国产91久久 | 欧美日韩综合一区 | 蜜臀久久99精品久久久久野外 | 高清人人天天夜夜曰狠狠狠狠 | 一区二区三区影院 | www.日日夜夜 | 一区二区精品视频 | 最新中文字幕一区 | 久久一区精品 | 亚洲国产伊人 | 国产在线一区二区三区 | 久久久久久av | 亚洲精品在线免费看 | 国产电影一区二区三区爱妃记 | 男人的天堂在线视频 | 久久国产三级 | 激情五月婷婷综合 | 一级毛片视频 | 欧美在线 | 一级毛片在线看 | 伊人久操 | 国产精品爱久久久久久久 | 懂色中文一区二区在线播放 | 欧洲精品久久久久毛片完整版 | 中文字幕一区二区三区四区五区 | 日日干夜夜草 | 日本字幕在线观看 | 精品国产精品 | 欧美在线日韩 | av一区二区在线观看 | 欧美精品在线免费观看 | 久久久久国产精品午夜一区 | 免费国产网站 |