Skip to content

Instantly share code, notes, and snippets.

@Mesabloo
Last active July 7, 2022 18:26
Show Gist options
  • Save Mesabloo/95e7bee4eba9bf75b59e9c5f67df212b to your computer and use it in GitHub Desktop.
Save Mesabloo/95e7bee4eba9bf75b59e9c5f67df212b to your computer and use it in GitHub Desktop.
A simple exercise with Haskell and genericity
{-| Exercise:
- Step 1:
Write a program simulating an iterating machine: we for example feed a range
@[0..4]@ and an action @print@ and it must execute this action on all the
elements of the range.
Writing @iterate1 [4..9] print@ must output:
> 4
> 5
> 6
> 7
> 8
> 9
- Step 2:
Now, we want to iterate on two ranges. Each action also has to take 2 parameters now.
We can consider using @(,)@ and having @type Action a b c = (a, b) -> c@.
Writing @iterate2 [1..3] [1..3] print@ must output:
> (1, 1)
> (1, 2)
> (1, 3)
> (2, 1)
> (2, 2)
> (2, 3)
> (3, 1)
> (3, 2)
> (3, 3)
- Step 3:
We would like to add filters on the ranges now. Any couple not satisfying the predicate should not be treated.
Writing @iterate2Predicate [1..3] [1..3] print (uncurry (==))@ must output:
> (1, 1)
> (2, 2)
> (3, 3)
- Step 4:
Let's make everything work on any number of ranges and any number of predicates
and any number of actions (both can possibly be 0).
Writing @iterateNPredicates [[1..3],[1..3]] [print] [\ [x, y] -> x == y, \ [x, _] -> even x]@ must output:
> [2, 2]
-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE KindSignatures #-}
import Control.Monad (void)
main :: IO ()
main = void $
mapsWithRangeAndPredicatesN
[[1..4],[1..4],[1..4]]
[print,print,print]
[\ (x:y:_) -> x == y, \ [_, _, z] -> even z]
-------------------------------------------------------------------------
mapWithRange1
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [a]
-> (a -> m b)
-> m [b]
mapWithRange1 range f = mapWithRangeAndPredicate1 range f (const True)
mapWithRangeAndPredicate1
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [a]
-> (a -> m b)
-> (a -> Bool)
-> m [b]
mapWithRangeAndPredicate1 range f pred =
mapWithRangeAndPredicateN [range] (f . listToTuple1) (pred . listToTuple1)
where listToTuple1 [x] = x
mapWithRange2
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [a]
-> [a]
-> (a -> a -> m b)
-> m [b]
mapWithRange2 range1 range2 f = mapWithRangeAndPredicate2 range1 range2 f (const (const True))
mapWithRangeAndPredicate2
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [a]
-> [a]
-> (a -> a -> m b)
-> (a -> a -> Bool)
-> m [b]
mapWithRangeAndPredicate2 range1 range2 f pred =
mapWithRangeAndPredicateN [range1, range2] (uncurryList2 f) (uncurryList2 pred)
where uncurryList2 g [x, y] = g x y
mapWithRangeN
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [[a]]
-> ([a] -> m b)
-> m [b]
mapWithRangeN ranges f = mapWithRangeAndPredicateN ranges f (const True)
mapWithRangeAndPredicateN
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [[a]]
-> ([a] -> m b)
-> ([a] -> Bool)
-> m [b]
mapWithRangeAndPredicateN ranges f pred = mapsWithRangeAndPredicateN ranges [f] pred
mapsWithRangeN
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [[a]]
-> [[a] -> m b]
-> m [b]
mapsWithRangeN ranges actions = mapsWithRangeAndPredicateN ranges actions (const True)
mapsWithRangeAndPredicateN
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [[a]]
-> [[a] -> m b]
-> ([a] -> Bool)
-> m [b]
mapsWithRangeAndPredicateN ranges actions pred = mapsWithRangeAndPredicatesN ranges actions [pred]
mapsWithRangeAndPredicatesN
:: forall (m :: * -> *) a b.
(Monad m, Enum a)
=> [[a]]
-> [[a] -> m b]
-> [[a] -> Bool]
-> m [b]
mapsWithRangeAndPredicatesN ranges actions preds = sequence [ f x | x <- cartN ranges, f <- actions, and $ ($ x) <$> preds ]
cartN :: [[a]] -> [[a]]
cartN [] = [ ]
cartN [l] = [ [x] | x <- l ]
cartN [l1, l2] = [ [x, y] | x <- l1, y <- l2 ]
cartN (l1:l2:r) = [ x <> y | x <- cartN [l1, l2], y <- cartN r ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment