Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
121 lines (96 sloc) 4.24 KB
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -Werror=incomplete-patterns #-}
Example on how to do compile-time (ie type-level) computations and how
to get the results into use on term-level (ie runtime).
The original problem is described in
[aoc 19 d6]( We use the described
example input data in a bit different form. See the link for the
original data and answer.
import qualified GHC.TypeLits as TL
import Data.Proxy
import Fcf
import Fcf.Data.Nat
import Fcf.Data.List as L
import Fcf.Alg.Morphism
import Fcf.Alg.List
import Fcf.Alg.Tree
-- | Type-level variable containing all the required information.
data OrbitInput :: Exp [(TL.Symbol, TL.Symbol)]
type instance Eval OrbitInput =
'[ '("COM", "B")
, '("B", "C")
, '("C", "D")
, '("D", "E")
, '("E", "F")
, '("B", "G")
, '("G", "H")
, '("D", "I")
, '("E", "J")
, '("J", "K")
, '("K", "L")]
-- helper, we might could do without this one
-- This is used to check that two symbols are same.
data EqPlanet :: TL.Symbol -> TL.Symbol -> Exp Bool
type instance Eval (EqPlanet a b) = Eval (TyEq 'EQ (TL.CmpSymbol a b))
-- | :kind! Eval (FindDescs "COM" =<< OrbitInput)
data FindDescs :: TL.Symbol -> [(TL.Symbol, TL.Symbol)] -> Exp [TL.Symbol]
type instance Eval (FindDescs p lst) =
Eval (Map Snd =<< (Filter (EqPlanet p <=< Fst) lst))
-- | This is not used in the final solution but worked as a step towards it.
-- :kind! Eval (Ana BuildOrbTreeCoA "COM")
data BuildOrbTreeCoA :: CoAlgebra (TreeF TL.Symbol) TL.Symbol
type instance Eval (BuildOrbTreeCoA nd) =
If (Eval (L.Null (Eval (FindDescs nd =<< OrbitInput))))
('NodeF nd '[])
('NodeF nd (Eval (FindDescs nd =<< OrbitInput)))
-- |
-- We need to store the depth of the node along the tree. We use the depth when
-- counting paths from root to every node.
-- :kind! Eval (Ana BuildOrbTreeCoA2 '(0,"COM"))
data BuildOrbTreeCoA2 :: CoAlgebra (TreeF (Nat,TL.Symbol)) (Nat,TL.Symbol)
type instance Eval (BuildOrbTreeCoA2 '(d,nd)) =
If (Eval (L.Null (Eval (FindDescs nd =<< OrbitInput))))
('NodeF '(d,nd) '[])
('NodeF '(d,nd) (Eval (MkPairLst (1 TL.+ d) =<< FindDescs nd =<< OrbitInput)))
-- helper, we might could do without this one
data MkPair :: Nat -> TL.Symbol -> Exp (Nat,TL.Symbol)
type instance Eval (MkPair n s) = '(n,s)
-- helper, we might could do without this one
data MkPairLst :: Nat -> [TL.Symbol] -> Exp [(Nat,TL.Symbol)]
type instance Eval (MkPairLst n lst) = Eval (Map (MkPair n) lst)
-- | Count paths from root to every node.
-- If it is a leaf, the number of paths from root to it is the depth.
-- If it is an internal node, the number of paths to the subtree rooted
-- by it is sum of pathnums of subtrees plus the depth (paths to this one).
data CountPathsAlg :: Algebra (TreeF (Nat,TL.Symbol)) Nat
type instance Eval (CountPathsAlg ('NodeF '(d,_) '[])) = d
type instance Eval (CountPathsAlg ('NodeF '(d,_) (b ': bs))) = d TL.+ (Eval (Sum (b ': bs)))
-- | We initialize this with the known starting node and the depth of it, which
-- is 0.
-- :kind! Eval OrbitSolution
data OrbitSolution :: Exp Nat
type instance Eval OrbitSolution =
Eval (Hylo CountPathsAlg BuildOrbTreeCoA2 '(0,"COM"))
-- | Helper to bring in the result of the type-level computation.
-- It is a nice exercise to solve this using Tree, UnfoldTree and FoldTree.
showResult :: forall n. (n ~ Eval OrbitSolution) => String
showResult = show $ TL.natVal @n Proxy
main :: IO ()
main = putStrLn $ "Number of orbits is " ++ showResult
You can’t perform that action at this time.