{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
{-# OPTIONS_GHC -fno-solve-constant-dicts #-}
module Data.List.Unrolled (
Append(..)
, Drop(..)
, Take(..)
, splitAt
, ChunksOf(..)
, ChunksCount
, Zip(..)
, Zip3(..)
, ZipWith(..)
, Unzip(..)
, Filter(..)
, Map(..)
, All(..)
, Foldr(..)
, Foldr1(..)
, Foldl(..)
, Foldl1(..)
, foldMap
, FoldMap
, sum
, Sum
, Replicate(..)
, EnumFromN(..)
, EnumFromStepN(..)
) where
import Data.Type.Bool (If)
import GHC.TypeLits (Nat, type (+), type (-), type (<=?))
import Prelude (Bool(..), otherwise, Num(..), error, Monoid(..), (.))
class Append (n :: Nat) where
append :: [a] -> [a] -> [a]
instance {-# OVERLAPPING #-} Append 0 where
append _ ys = ys
{-# INLINE append #-}
instance {-# OVERLAPPABLE #-} (Append (n - 1)) => Append n where
append [] _ = error "append: Not enough elements in the list."
append (x : xs) ys = x : append @(n - 1) xs ys
{-# INLINE append #-}
class Drop (n :: Nat) where
drop :: [a] -> [a]
instance {-# OVERLAPPING #-} Drop 0 where
drop xs = xs
{-# INLINE drop #-}
instance {-# OVERLAPPABLE #-} (Drop (n - 1)) => Drop n where
drop [] = error "drop: Not enough elements in the list."
drop (_ : xs) = drop @(n - 1) xs
{-# INLINE drop #-}
class Take (n :: Nat) where
take :: [a] -> [a]
instance {-# OVERLAPPING #-} Take 0 where
take _ = []
{-# INLINE take #-}
instance {-# OVERLAPPABLE #-} (Take (n - 1)) => Take n where
take [] = error "take: Not enough elements in the list."
take (x : xs) = x : take @(n - 1) xs
{-# INLINE take #-}
splitAt :: forall (n :: Nat) a. (Take n, Drop n) => [a] -> ([a], [a])
splitAt xs = (take @n xs, drop @n xs)
class ChunksOf (n :: Nat) (c :: Nat) where
chunksOf :: [a] -> [[a]]
instance {-# OVERLAPPING #-} ChunksOf 0 0 where
chunksOf _ = []
{-# INLINE chunksOf #-}
instance {-# OVERLAPPABLE #-} ChunksOf 0 c where
chunksOf _ = []
{-# INLINE chunksOf #-}
instance {-# OVERLAPPABLE #-} ChunksOf n 0 where
chunksOf _ = []
{-# INLINE chunksOf #-}
instance {-# OVERLAPPABLE #-} (Take c, Drop c, ChunksOf (n - 1) c) => ChunksOf n c where
chunksOf xs =
let (l, r) = splitAt @c xs
in l : chunksOf @(n - 1) @c r
{-# INLINE chunksOf #-}
type family ChunksCount (len :: Nat) (clen :: Nat) where
ChunksCount 0 _ = 0
ChunksCount _ 0 = 0
ChunksCount l c = If (l <=? c) 1 (1 + ChunksCount (l - c) c)
class Zip (n :: Nat) where
zip :: [a] -> [b] -> [(a, b)]
instance {-# OVERLAPPING #-} Zip 0 where
zip _ _ = []
{-# INLINE zip #-}
instance {-# OVERLAPPABLE #-} (Zip (n - 1)) => Zip n where
zip (x : xs) (y : ys) = (x, y) : zip @(n - 1) xs ys
zip (_ : _ ) [] = []
zip [] _ = error "zip: Not enough elements in the first list."
{-# INLINE zip #-}
class Zip3 (n :: Nat) where
zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
instance {-# OVERLAPPING #-} Zip3 0 where
zip3 _ _ _ = []
{-# INLINE zip3 #-}
instance {-# OVERLAPPABLE #-} (Zip3 (n - 1)) => Zip3 n where
zip3 (x : xs) (y : ys) (z : zs) = (x, y, z) : zip3 @(n - 1) xs ys zs
zip3 (_ : _ ) [] _ = []
zip3 (_ : _ ) _ [] = []
zip3 [] _ _ = error "zip3: Not enough elements in the first list."
{-# INLINE zip3 #-}
class Unzip (n :: Nat) where
unzip :: [(a, b)] -> ([a], [b])
instance {-# OVERLAPPING #-} Unzip 0 where
unzip _ = ([], [])
{-# INLINE unzip #-}
instance {-# OVERLAPPABLE #-} (Unzip (n - 1)) => Unzip n where
unzip [] = error "unzip: Not enough elements in the list."
unzip (x : xs) = (\(a, b) (as, bs) -> (a : as, b : bs)) x (unzip @(n - 1) xs)
{-# INLINE unzip #-}
class ZipWith (n :: Nat) where
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
instance {-# OVERLAPPING #-} ZipWith 0 where
zipWith _ _ _ = []
{-# INLINE zipWith #-}
instance {-# OVERLAPPABLE #-} (ZipWith (n - 1)) => ZipWith n where
zipWith f (x : xs) (y : ys) = f x y : zipWith @(n - 1) f xs ys
zipWith _ (_ : _ ) [] = []
zipWith _ [] _ = error "zipWith: Not enough elements in the first list."
{-# INLINE zipWith #-}
class Filter (n :: Nat) where
filter :: (a -> Bool) -> [a] -> [a]
instance {-# OVERLAPPING #-} Filter 0 where
filter _ _ = []
{-# INLINE filter #-}
instance {-# OVERLAPPABLE #-} (Filter (n - 1)) => Filter n where
filter _ [] = error "filter: Not enough elements in the list."
filter f (x : xs)
| f x = x : filter @(n - 1) f xs
| otherwise = filter @(n - 1) f xs
{-# INLINE filter #-}
class Map (n :: Nat) where
map :: (a -> b) -> [a] -> [b]
instance {-# OVERLAPPING #-} Map 0 where
map _ _ = []
{-# INLINE map #-}
instance {-# OVERLAPPABLE #-} (Map (n - 1)) => Map n where
map _ [] = error "map: Not enough elements in the list."
map f (x : xs) = f x : map @(n - 1) f xs
{-# INLINE map #-}
class All (n :: Nat) where
all :: (a -> Bool) -> [a] -> Bool
instance {-# OVERLAPPING #-} All 0 where
all _ _ = True
{-# INLINE all #-}
instance {-# OVERLAPPABLE #-} (All (n - 1)) => All n where
all _ [] = error "all: Not enough elements in the list."
all f (x : xs)
| f x = all @(n - 1) f xs
| otherwise = False
{-# INLINE all #-}
class Foldr (n :: Nat) where
foldr :: (a -> b -> b) -> b -> [a] -> b
instance {-# OVERLAPPING #-} Foldr 0 where
foldr _ z _ = z
{-# INLINE foldr #-}
instance {-# OVERLAPPABLE #-} (Foldr (n - 1)) => Foldr n where
foldr _ _ [] = error "foldr: Not enough elements in the list."
foldr f z (x : xs) = f x (foldr @(n - 1) f z xs)
{-# INLINE foldr #-}
class Foldr1 (n :: Nat) where
foldr1 :: (a -> a -> a) -> [a] -> a
instance {-# OVERLAPPING #-} Foldr1 1 where
foldr1 _ [] = error "foldr1: Not enough elements in the list."
foldr1 _ (x : _) = x
{-# INLINE foldr1 #-}
instance {-# OVERLAPPABLE #-} (Foldr1 (n - 1)) => Foldr1 n where
foldr1 _ [] = error "foldr1: Empty list."
foldr1 f (x : xs) = f x (foldr1 @(n - 1) f xs)
{-# INLINE foldr1 #-}
class Foldl (n :: Nat) where
foldl :: (b -> a -> b) -> b -> [a] -> b
instance {-# OVERLAPPING #-} Foldl 0 where
foldl _ z _ = z
{-# INLINE foldl #-}
instance {-# OVERLAPPABLE #-} (Foldl (n - 1)) => Foldl n where
foldl _ _ [] = error "foldl: Not enough elements in the list."
foldl f z (x : xs) = f (foldl @(n - 1) f z xs) x
{-# INLINE foldl #-}
class Foldl1 (n :: Nat) where
foldl1 :: (a -> a -> a) -> [a] -> a
instance {-# OVERLAPPING #-} Foldl1 1 where
foldl1 _ [] = error "foldl1: Not enough elements in the list."
foldl1 _ (x : _) = x
{-# INLINE foldl1 #-}
instance {-# OVERLAPPABLE #-} (Foldl1 (n - 1)) => Foldl1 n where
foldl1 _ [] = error "foldl1: Empty list."
foldl1 f (x : xs) = f (foldl1 @(n - 1) f xs) x
{-# INLINE foldl1 #-}
foldMap :: forall (n :: Nat) m a.
(FoldMap n m) =>
(a -> m) -> [a] -> m
foldMap f = foldr @n (mappend . f) mempty
{-# INLINE foldMap #-}
type FoldMap (n :: Nat) m = (Monoid m, Foldr n)
sum :: forall (n :: Nat) a.
(Sum n a) =>
[a] -> a
sum = foldr @n (+) 0
{-# INLINE sum #-}
type Sum (n :: Nat) a = (Foldr n, Num a)
class Replicate (n :: Nat) where
replicate :: a -> [a]
instance {-# OVERLAPPING #-} Replicate 0 where
replicate _ = []
{-# INLINE replicate #-}
instance {-# OVERLAPPABLE #-} (Replicate (n - 1)) => Replicate n where
replicate a = a : replicate @(n - 1) a
{-# INLINE replicate #-}
class EnumFromN (n :: Nat) where
enumFromN :: (Num a)
=> a
-> [a]
instance {-# OVERLAPPING #-} EnumFromN 0 where
enumFromN _ = []
{-# INLINE enumFromN #-}
instance {-# OVERLAPPABLE #-} (EnumFromN (n - 1)) => EnumFromN n where
enumFromN a = a : enumFromN @(n - 1) (a + 1)
{-# INLINE enumFromN #-}
class EnumFromStepN (n :: Nat) where
enumFromStepN :: (Num a)
=> a
-> a
-> [a]
instance {-# OVERLAPPING #-} EnumFromStepN 0 where
enumFromStepN _ _ = []
{-# INLINE enumFromStepN #-}
instance {-# OVERLAPPABLE #-} (EnumFromStepN (n - 1)) => EnumFromStepN n where
enumFromStepN a s = a : enumFromStepN @(n - 1) (a + s) s
{-# INLINE enumFromStepN #-}