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 ())

问题:

  1. 我该如何解决这个问题?谁能给我解释一下哪里出了问题?
  2. 我是否应该从之前的练习中推断出额外的规则(我在这里是否遗漏了一些可组合性规则)?
// validateEmail :: Email -> Either String Email
// addToMailingList :: Email -> IO([Email])
// emailBlast :: [Email] -> IO ()

意味着你不能使用Either.chain,你必须map覆盖组合中第一个函数的Either String (validateEmail)returns。如您所知,“我无法使用链组合它们,因为函数 B 没有任何需要展平的内容。”。

还要注意我们这里有两个个单子,两个不同个单子:EitherIOchain 不适用于 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)
}