Generic mapN and zipN
milesfrain opened this issue · 3 comments
Proposing we add these mapN functions, and make them available to more than just List.
map2 :: forall a b c f. Functor f => (a -> b -> c) -> f a -> f b -> f c
map3 :: forall a b c d f. Functor f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
...
map10 ...We can then use the mapN functions to create zipN functions:
-- This already exists, but it would be nice to offer a more generic version:
zip :: forall a b f. Functor f => f a -> f b -> Tuple a b
zip = map2 Tuple
-- Likely needed to fill the gap between the seemingly-identical
-- (but actually different) `Tuple` and nested `Tuple2`.
zip2 :: forall a b f. Functor f => f a -> f b -> f (Tuple2 a b)
zip2 = map2 Tuple2
zip3 :: forall a b c f. Functor f => f a -> f b -> f c -> f (Tuple3 a b c)
zip3 = map3 Tuple3
...
zip10 :: ...
zip10 = map10 Tuple10We probably need a different typeclass than Functor though. What about Zippable?
A motivation for adding these functions is to get closer to Elm's usability in this area:
https://package.elm-lang.org/packages/elm/core/latest/List#map2
My understanding of the current best way to get map3 behavior is to take a detour though nested tuples and create a custom zip3 along the way. Lots of hoops to jump through and the dependencies seem inverted (zip3 should depend on map3).
module Main where
import Prelude
import Data.Array (zip)
import Data.Tuple.Nested (uncurry3, Tuple3)
import Effect (Effect)
import Effect.Console (logShow)
import TryPureScript (render, withConsole)
zip3 :: forall a b c. Array a -> Array b -> Array c -> Array (Tuple3 a b c)
zip3 a b c = zip a $ zip b $ zip c $ map (const unit) a
map3 :: forall a b c d f. (a -> b -> c -> d) -> Array a -> Array b -> Array c -> Array d
map3 fn a b c = map (uncurry3 fn) $ zip3 a b c
arrA = [ 1, 2, 3 ] :: Array Int
arrB = [ 4, 5, 6 ] :: Array Int
arrC = [ 7, 8, 9 ] :: Array Int
myFunc :: Int -> Int -> Int -> Int
myFunc a b c = a * b + c
main :: Effect Unit
main = render =<< withConsole do
logShow $ zip3 arrA arrB arrC
logShow $ map3 myFunc arrA arrB arrCZippable things are alternative Applicative instances, it's just that the chosen instance for List/Array does not zip, but models non-determinism, as that behavior also admits a Monad instance. The ZipList data type accomplishes general-purpose n-ary zipping for lists through it's Applicative instance (eg Tuple <$> zipList1 <*> zipList2).
purescript/purescript-arrays#163 See this ticket which mentions adding a ZipArray for arrays. This is possible, it just has not been implemented and PR'ed.