$ whoami
`Money` has a monoid instance
-- addMoney a (addMoney b c) == (addMoney (addMoney a b) c)
addMoney :: Money -> Money -> Money
zeroMoney :: Money
traverse
?Promise.all
Promise.all([p1, p2, p3])
.then(([v1, v2, v3]) => {
console.log("Got values", v1, v2, v3);
});
Promise.all
const myMap = new Map([
["p1", p1], ["p2", p2], ["p3", p3]
]);
// any iterable!
Promise.all(myMap)
.then((vs) => {
console.log("Got values", vs);
});
Promise.all
Promise.all
is nice because it’s not hardcoded with lists.Promise.all
(except async values in haskell are lazy, not eager as JS promises)
Promise.all([p1, p2, p3])
.then(([v1, v2, v3]) => {
console.log("Got values", v1, v2, v3);
});
Promise.all(userIds.map(getUser))
.then(users => …);
userIds :: [UserId]
userIds = ["1", "2", "3"]
getUser :: UserId -> IO User
getUser uid = …
allUsers :: IO [User]
allUsers = traverse getUser userIds
Promise.all
?
getUsers :: Map Role [UserId]
-> IO (Map Role [User])
getUsers usersMap =
getCompose
(traverse getUser (Compose usersMap))
Traversable
.Promise.all
is a super useful functionTraversable
.IO
from “inside” the t
, to “outside” it.t
that is traversable
Traversable
can be derived automatically for many types.
data BinaryTree a
= Leaf a
| Node (BinaryTree a) (BinaryTree a)
deriving (Eq, Show, Ord,
Functor, Foldable,
Traversable)
Foldable
is interesting because it captures a bit the idea of promise.allPromise.all
traverse
?traverse_
!traverse
. This was a lie.
traverse
, for real
parseInt :: String -> Validation [Error] Int
parseInt = …
parseValues :: [String] -> Validation [Error] [Int]
parseValues values =
traverse parseInt values
checks :: [String -> Validation [Error] ()]
checks = …
checkValue :: String -> Validation [Error] ()
checkValue v =
traverse_ (\check -> check v) checks
parseString :: String -> Parser String
parseString = …
parseVariables :: [String]
-> Parser [String]
parseVariables names =
traverse parseString names
readFile :: FilePath -> IO String
readFile = …
parseFile :: String -> Validation [Errors] Value
parseFile = …
parseFiles :: [FilePath]
-> IO (Validation [Errors] [Value])
parseFiles paths =
let readAndParse path =
Compose (fmap parseFile (readFile path))
in getCompose (traverse readAndParse paths)
IO
and Validation
)Compose
and getCompose
, but apart from that, it’s a regular traverse
(a, Maybe b) -> Maybe (a, b)
Either a [b] -> [Either a b]
[Either a b] -> Either a [b]
[a -> b] -> (a -> [b])
sequenceA
calls just tell you that the inversions areContext ->