Monadic组合练习错误
Monadic composition exercise wrong
背景
我正在阅读大部分 Adequate Guide to Functional Programming 并进行所有练习。我在第 9 章,Monadic Onions 但我正在努力练习。
练习 1
Considering a User object as follow, use safeProp
and map/join
or chain
to safely get the street name when given a user:
// safeProp :: String -> Object -> Maybe a
const safeProp = curry((p, obj) => compose(Maybe.of, prop(p))(obj));
const user = {
id: 1,
name: 'Albert',
address: {
street: {
number: 22,
name: 'Walnut St',
},
},
};
解决方案 1
// getStreetName :: User -> Maybe String
const getStreetName = compose(
chain( safeProp( "name" ) ),
chain( safeProp( "street" ) ),
safeProp( "address" )
);
这个很简单。 safeProp
return 是一个 Maybe Monad。因此,在组合 safeProp
时,我们需要使用 chain
(又名 flatMap
)来跟进,否则我们将得到 [=23= 而不是 Maybe.of("value")
].
规则推断:如果你想组合函数 A 和函数 B,以及两个 return Monads,使用 chain
!
练习 2
Given the following functions, use getFile to get the filepath, remove the directory and keep only the basename, then purely log it. Hint: you may want to use split
and last
to obtain the basename from a filepath.
// getFile :: () -> IO String
const getFile = () => IO.of('/home/mostly-adequate/ch9.md');
// pureLog :: String -> IO ()
const pureLog = str => new IO(() => console.log(str));
解决方案 2
const getBaseName = compose( last, split("/") );
const logFilename = compose(
chain( pureLog ),
map( getBaseName ),
getFile
);
这个有点棘手,但我也搞定了。
所以,getFile
return 是一个 IO Monad。但是 getBaseMap
return 只是一个字符串。
因此,我有 return 是 Monad 的函数 A 和 return 是原始类型的函数 B。我无法使用 chain
组合它们,因为函数 B 没有任何需要展平的内容。这意味着我需要 map
来组合 A 和 B!
又一个规则!
现在,我需要将 B 与 pureLog ( C ) 组合起来。
在 B 上应用映射后,它将 return 具有转换值的 IO Monad。让我们称之为 MB。鉴于我需要用 C 组合 MB(return 是一个 monad)我不能应用规则 1 并简单地使用 chain
.
呸!
让我们进入最后一个!
练习 3:
Given the following functions, use validateEmail
, addToMailingList
and emailBlast
to create a function which adds a new email to the mailing list if valid, and then notify the whole list.
// validateEmail :: Email -> Either String Email
// addToMailingList :: Email -> IO([Email])
// emailBlast :: [Email] -> IO ()
解决方案 3?
我不知道怎么做这个....
这是我目前所做的:
// joinMailingList :: Email -> Either String (IO ())
const joinMailingList = compose(
chain( emailBlast ),
chain( addToMailingList ),
validateEmail
);
但这是错误的。我收到以下错误:
The function has an invalid type; hint: joinMailingList
should return an Either String (IO ())
问题:
- 我该如何解决这个问题?谁能给我解释一下哪里出了问题?
- 我是否应该从之前的练习中推断出额外的规则(我在这里是否遗漏了一些可组合性规则)?
// validateEmail :: Email -> Either String Email
// addToMailingList :: Email -> IO([Email])
// emailBlast :: [Email] -> IO ()
意味着你不能使用Either.chain
,你必须map
覆盖组合中第一个函数的Either String
(validateEmail
)returns。如您所知,“我无法使用链组合它们,因为函数 B 没有任何需要展平的内容。”。
还要注意我们这里有两个个单子,两个不同个单子:Either
和IO
。 chain
不适用于 any monad,它仅适用于其两个参数中的 same monadic 类型。每个 monad(即每个 monad 类型)都有自己的 chain
方法。使用单个 chain
函数只是一种抽象,它利用了运行时多态性(或者编译时多态性,如果语言有支持它的编译器)。所以我们需要
function joinMailingList(email) {
return Either.map(validateEmail(email), addAndBlast)
}
function addAndBlast(email) {
return IO.chain(addToMailingList(email), emailBlast)
}
背景
我正在阅读大部分 Adequate Guide to Functional Programming 并进行所有练习。我在第 9 章,Monadic Onions 但我正在努力练习。
练习 1
Considering a User object as follow, use
safeProp
andmap/join
orchain
to safely get the street name when given a user:// safeProp :: String -> Object -> Maybe a const safeProp = curry((p, obj) => compose(Maybe.of, prop(p))(obj)); const user = { id: 1, name: 'Albert', address: { street: { number: 22, name: 'Walnut St', }, }, };
解决方案 1
// getStreetName :: User -> Maybe String
const getStreetName = compose(
chain( safeProp( "name" ) ),
chain( safeProp( "street" ) ),
safeProp( "address" )
);
这个很简单。 safeProp
return 是一个 Maybe Monad。因此,在组合 safeProp
时,我们需要使用 chain
(又名 flatMap
)来跟进,否则我们将得到 [=23= 而不是 Maybe.of("value")
].
规则推断:如果你想组合函数 A 和函数 B,以及两个 return Monads,使用 chain
!
练习 2
Given the following functions, use getFile to get the filepath, remove the directory and keep only the basename, then purely log it. Hint: you may want to use
split
andlast
to obtain the basename from a filepath.// getFile :: () -> IO String const getFile = () => IO.of('/home/mostly-adequate/ch9.md'); // pureLog :: String -> IO () const pureLog = str => new IO(() => console.log(str));
解决方案 2
const getBaseName = compose( last, split("/") );
const logFilename = compose(
chain( pureLog ),
map( getBaseName ),
getFile
);
这个有点棘手,但我也搞定了。
所以,getFile
return 是一个 IO Monad。但是 getBaseMap
return 只是一个字符串。
因此,我有 return 是 Monad 的函数 A 和 return 是原始类型的函数 B。我无法使用 chain
组合它们,因为函数 B 没有任何需要展平的内容。这意味着我需要 map
来组合 A 和 B!
又一个规则!
现在,我需要将 B 与 pureLog ( C ) 组合起来。
在 B 上应用映射后,它将 return 具有转换值的 IO Monad。让我们称之为 MB。鉴于我需要用 C 组合 MB(return 是一个 monad)我不能应用规则 1 并简单地使用 chain
.
呸!
让我们进入最后一个!
练习 3:
Given the following functions, use
validateEmail
,addToMailingList
andemailBlast
to create a function which adds a new email to the mailing list if valid, and then notify the whole list.// validateEmail :: Email -> Either String Email // addToMailingList :: Email -> IO([Email]) // emailBlast :: [Email] -> IO ()
解决方案 3?
我不知道怎么做这个....
这是我目前所做的:
// joinMailingList :: Email -> Either String (IO ())
const joinMailingList = compose(
chain( emailBlast ),
chain( addToMailingList ),
validateEmail
);
但这是错误的。我收到以下错误:
The function has an invalid type; hint:
joinMailingList
should return an Either String (IO ())
问题:
- 我该如何解决这个问题?谁能给我解释一下哪里出了问题?
- 我是否应该从之前的练习中推断出额外的规则(我在这里是否遗漏了一些可组合性规则)?
// validateEmail :: Email -> Either String Email // addToMailingList :: Email -> IO([Email]) // emailBlast :: [Email] -> IO ()
意味着你不能使用Either.chain
,你必须map
覆盖组合中第一个函数的Either String
(validateEmail
)returns。如您所知,“我无法使用链组合它们,因为函数 B 没有任何需要展平的内容。”。
还要注意我们这里有两个个单子,两个不同个单子:Either
和IO
。 chain
不适用于 any monad,它仅适用于其两个参数中的 same monadic 类型。每个 monad(即每个 monad 类型)都有自己的 chain
方法。使用单个 chain
函数只是一种抽象,它利用了运行时多态性(或者编译时多态性,如果语言有支持它的编译器)。所以我们需要
function joinMailingList(email) {
return Either.map(validateEmail(email), addAndBlast)
}
function addAndBlast(email) {
return IO.chain(addToMailingList(email), emailBlast)
}