简体   繁体   中英

Is the JavaScript `then` the same as Haskell `fmap`?

In JavaScript, Promises have a method called then , which is used to unpack the result in case of success, for example,

fetch("google.com").then(console.log)

From this Haskell's tutorial , I also found a similar thing which is called fmap , for example,

fmap putStrLn (fetch "google.com")

They look pretty similar, but I am not sure if they are equivalent . That's why I wanted to ask if they are the same thing.

PS: The term equivalent shall be that of the Curry-Howard Correspondence kind of equivalent.

They are related, yes. But then for Promise s does several different things that in Haskell would be separate functions, not all from the Functor class (the one that provides fmap ).

In Haskell, Promise would be a type constructor parameterized by the type of what it eventually returns, like Promise Int or Promise String .

We could make that type an instance of Functor , giving us fmap :: (a -> b) -> Promise a -> Promise b . This would let us map a pure computation over the result eventually returned by the promise. But it wouldn't let us chain promises! If we try fmapping with a function that returns a promise, say of type Int -> Promise String , we would end up with a Promise that returned another Promise at the end but did not execute it, which is not what we usually want.

We could also make Promise an instance of Monad . Monad is a subclass of Functor . All Monad s are Functor s, but not all Functor s are Monad s. Monad would give us the function >>= (usually called "bind") that would have type (>>=) :: Promise a -> (a -> Promise b) -> Promise b . This would be analogous to a then in which the callback returns another Promise that is sequenced after the original one.

Ignoring typeclasses, we have the following types in Haskell (we will say having the right Haskell typeclasses corresponds to having a suitable .then method in JavaScript):

fmap :: (a -> b) -> f a -> f b
bind :: (a -> f b) -> f a -> f b

And in JavaScript we have (made up syntax):

.then :: (this :: f a) -> (a -> (b || f b)) -> f b

So in one sense they are equivalent, but in another not. For example, suppose some kind of promise type called P in Haskell and we want to read a URL from a file and then give a promise of fetching that URL:

read :: String -> P String
fetch :: String -> P String
readFetch :: String -> P (P String)
readFetch file = fmap fetch (read file)

And then later you might do :

fetched <- readFetch someFile
...
foo <- fetched

While in JavaScript if you did read(file).then(fetch) this would be equivalent to the following Haskell:

readFetch :: String -> P String
readFetch file = bind fetch (read file)

So the first only becomes fulfilled once the file is read but the second once the fetch is complete (ie later).

We conclude that then is similar but not exactly the same as fmap .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM