-- |
-- Module : Data.Semigroup.Numbered
-- Copyright : (c) Justus Sagemüller 2017
-- License : LGPL v3
--
-- Maintainer : (@) jsagemue $ uni-koeln.de
-- Stability : experimental
-- Portability : portable
--
{-# LANGUAGE DataKinds, KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
module Data.Semigroup.Numbered ( SemigroupNo(..)
-- * The common directions
, SemigroupX, SemigroupY, SemigroupZ
-- * Infix ops (diagrams/hmatrix style)
, (|||), (===)
-- * Infix ops (ASCII-art style)
, (│), (──), (■), (┃), (━━), (██)
) where
import GHC.TypeLits
import qualified Data.List.NonEmpty as NE
import Data.Foldable
import Data.Proxy
import Data.Void
import Data.CallStack (HasCallStack)
class SemigroupNo (n :: Nat) g where
sappendN :: proxy n -> g -> g -> g
sappendN p x y = sconcatN p $ x NE.:|[y]
sconcatN :: proxy n -> NE.NonEmpty g -> g
sconcatN = foldr1 . sappendN
stimesN :: (Integral b, HasCallStack) => proxy n -> b -> g -> g
-- Adapted from
-- http://hackage.haskell.org/package/base-4.10.0.0/docs/src/Data.Semigroup.html#Semigroup
stimesN p y₀ x₀
| y₀ <= 0 = error "stimesN: positive multiplier expected"
| otherwise = f x₀ y₀
where
f x y
| even y = f (x <> x) (y `quot` 2)
| y == 1 = x
| otherwise = g (x <> x) (pred y `quot` 2) x
g x y z
| even y = g (x <> x) (y `quot` 2) z
| y == 1 = x <> z
| otherwise = g (x <> x) (pred y `quot` 2) (x <> z)
(<>) = sappendN p
instance (SemigroupNo n g) => SemigroupNo n (a -> g) where
sappendN p f g x = sappendN p (f x) (g x)
sconcatN p fs x = sconcatN p $ ($x)<$>fs
stimesN p n f = stimesN p n . f
instance (SemigroupNo n g) => SemigroupNo n (Maybe g) where
sappendN _ Nothing b = b
sappendN _ a Nothing = a
sappendN p (Just a) (Just b) = Just $ sappendN p a b
stimesN _ _ Nothing = Nothing
stimesN p n (Just a) = Just $ stimesN p n a
instance SemigroupNo n () where
sappendN _ () () = ()
sconcatN _ _ = ()
stimesN _ _ () = ()
instance SemigroupNo n (Proxy x) where
sappendN _ Proxy Proxy = Proxy
sconcatN _ _ = Proxy
stimesN _ _ Proxy = Proxy
instance SemigroupNo n Void where
sappendN _ = absurd
stimesN _ _ = absurd
instance SemigroupNo 0 [Void] where sappendN _ [] [] = []
instance SemigroupNo 0 [()] where sappendN _ = (++)
instance SemigroupNo 0 [Char] where sappendN _ = (++)
instance SemigroupNo 0 [Int] where sappendN _ = (++)
instance SemigroupNo 0 [Integer] where sappendN _ = (++)
instance SemigroupNo 0 [Float] where sappendN _ = (++)
instance SemigroupNo 0 [Double] where sappendN _ = (++)
instance SemigroupNo 0 [Rational] where sappendN _ = (++)
instance SemigroupNo 0 [Maybe a] where sappendN _ = (++)
instance (SemigroupNo 0 [a]) => SemigroupNo 0 [[a]] where
sappendN _ [] ys = ys
sappendN _ xs [] = xs
sappendN p (x:xs) (y:ys) = sappendN p x y : sappendN p xs ys
instance SemigroupNo 1 [[Void]] where sappendN _ = (++)
instance SemigroupNo 1 [[()]] where sconcatN _ = paddedLines () . concat
instance SemigroupNo 1 [[Char]] where sconcatN _ = paddedLines ' ' . concat
instance SemigroupNo 1 [[Int]] where sconcatN _ = paddedLines 0 . concat
instance SemigroupNo 1 [[Integer]] where sconcatN _ = paddedLines 0 . concat
instance SemigroupNo 1 [[Float]] where sconcatN _ = paddedLines 0 . concat
instance SemigroupNo 1 [[Double]] where sconcatN _ = paddedLines 0 . concat
instance SemigroupNo 1 [[Rational]] where sconcatN _ = paddedLines 0 . concat
instance SemigroupNo 1 [[Maybe a]] where sconcatN _ = paddedLines Nothing . concat
instance (SemigroupNo 1 [[a]]) => SemigroupNo 1 [[[a]]] where
sappendN _ [] ys = ys
sappendN _ xs [] = xs
sappendN p (x:xs) (y:ys) = sappendN p x y : sappendN p xs ys
paddedLines :: a -> [[a]] -> [[a]]
paddedLines padr xs = mkPadded <$> xs
where mkPadded cs = cs ++ replicate (paddingLen - length cs) padr
paddingLen = maximum $ length <$> xs
type SemigroupX = SemigroupNo 0
infixr 6 │
-- | Horizontal concatenation. Fixity as of
-- <http://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Semigroup.html#v:-60--62- the standard semigroup>.
--
-- @U+2502@ / Vim digraph @vv@.
(│) :: SemigroupX g => g -> g -> g
(│) = sappendN (Proxy :: Proxy 0)
infixr 3 ┃
-- | Horizontal concatenation. @U+2503@ / Vim digraph @VV@.
(┃) :: SemigroupX g => g -> g -> g
(┃) = sappendN (Proxy :: Proxy 0)
infixl 6 |||
-- | Horizontal concatenation. Fixity as
-- <http://hackage.haskell.org/package/diagrams-lib-1.4.1.2/docs/Diagrams-TwoD-Combinators.html#v:-61--61--61- in diagrams>.
(|||) :: SemigroupX g => g -> g -> g
(|||) = sappendN (Proxy :: Proxy 0)
type SemigroupY = SemigroupNo 1
infixr 5 ──
-- | Vertical concatenation. @U+2500@ / Vim digraph @hh@.
(──) :: SemigroupY g => g -> g -> g
(──) = sappendN (Proxy :: Proxy 1)
infixr 2 ━━
-- | Vertical concatenation. @U+2501@ / Vim digraph @HH@.
(━━) :: SemigroupY g => g -> g -> g
(━━) = sappendN (Proxy :: Proxy 1)
infixl 6 ===
-- | Vertical concatenation. Fixity as
-- <http://hackage.haskell.org/package/diagrams-lib-1.4.1.2/docs/Diagrams-TwoD-Combinators.html#v:-124--124--124- in diagrams>.
(===) :: SemigroupY g => g -> g -> g
(===) = sappendN (Proxy :: Proxy 1)
type SemigroupZ = SemigroupNo 2
infixr 4 ■
-- | z-concatenation. @U+25A0@ / Vim digraph @fS@.
(■) :: SemigroupZ g => g -> g -> g
(■) = sappendN (Proxy :: Proxy 2)
infixr 1 ██
-- | z-concatenation. @U+254B@ / Vim digraph @FB@.
(██) :: SemigroupZ g => g -> g -> g
(██) = sappendN (Proxy :: Proxy 2)