LoginSignup
2
3

More than 5 years have passed since last update.

recursion-schemeをGADTとDataKindsで

Posted at

recursion-schemeではdata familiesを用いている為形状の違う関手毎に別のデータ構築子を用意してやらなければいけない。
そこでDependently Typed Programming in Agdaの3.2の手法をHaskellで実装した。
WrapFunctorは型族Evalに対して部分適用を行う為のラッパーである。

{-# LANGUAGE GADTs, TypeOperators, KindSignatures, TypeFamilies, DataKinds, PolyKinds, RankNTypes, FlexibleInstances, TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}
module Main where

import Unsafe.Coerce
import Numeric.Natural
import Test.QuickCheck

-- 関手の形状を定義するDSL
data Univ where
    (:+:) :: Univ -> Univ -> Univ
    (:*:) :: Univ -> Univ -> Univ
    Id :: Univ
    K :: (a :: *) -> Univ

infixr 7 :*:
infixr 6 :+:

-- Univと対象の型からUnivを実際の関手として適用した型を返す
type family Eval (u :: Univ) a :: *
type instance Eval Id a = a
type instance Eval (K b) a = b
type instance Eval (a :+: b) c = Either (Eval a c) (Eval b c)
type instance Eval (a :*: b) c = (Eval a c, Eval b c)

-- 不動点型
data Mu (u :: Univ) where
    InF :: Eval u (Mu u) -> Mu u

outF :: Mu u -> Eval u (Mu u)
outF (InF a) = a

-- type familyに対して部分適用する為のラッパー
newtype WrapFunctor u a = WrapFunctor { unwrapFunctor :: Eval u a }

instance Functor (WrapFunctor Id) where
    fmap f (WrapFunctor x) = WrapFunctor $ f x

instance Functor (WrapFunctor (K a)) where
    fmap _ (WrapFunctor a) = WrapFunctor $ unsafeCoerce $ a -- どうしても通らなかった
instance (Functor (WrapFunctor u), Functor (WrapFunctor v)) =>
    Functor (WrapFunctor (u :+: v)) where fmap = fmap_sum

instance (Functor (WrapFunctor u), Functor (WrapFunctor v)) =>
    Functor (WrapFunctor (u :*: v)) where fmap = fmap_prod

-- F代数
type Alg u a = Eval u a -> a
-- F余代数
type CoAlg u a = a -> Eval u a

-- aに対してUnivで定義できる型を対応付ける
type family Base a :: Univ

-- 始代数の逆射
class Functor (WrapFunctor (Base a)) => Initial a where
    proj :: a -> Eval (Base a) a

-- 終余代数の逆射
class Functor (WrapFunctor (Base a)) => Terminal a where
    embed :: Eval (Base a) a  -> a

-- Nat =~ μ X. 1 + X
type instance Base Natural = K () :+: Id

instance Initial Natural where
    proj 0 = Left ()
    proj n = Right (pred n)

instance Terminal Natural where
    embed (Left _) = 0
    embed (Right n) = succ n

-- [a] =~ μ X. 1 + a * X
type instance Base [a] = K () :+: K a :*: Id

instance Initial [a] where
    proj [] = Left ()
    proj (x:xs) = Right (x,xs)

instance Terminal [a] where
    embed (Left ()) = []
    embed (Right (x,xs)) = x:xs

-- 明示的に型シグニチャを指定しないと型が合わない
cata :: forall a b. (Initial a) => Alg (Base a) b -> a -> b
cata beta = beta' . fmap (cata beta) . proj'
    where proj' :: a -> WrapFunctor (Base a) a
          proj' = WrapFunctor . proj
          beta' = beta . unwrapFunctor

ana :: forall a b. Terminal a => CoAlg (Base a) b -> b -> a
ana delta = embed' . fmap (ana delta) . delta'
    where delta' = WrapFunctor . delta
          embed' :: WrapFunctor (Base a) a -> a
          embed' = embed . unwrapFunctor

main = do
  quickCheck $ \ (xs :: [Int]) ->  cata (either (const []) (uncurry (:))) xs == xs
  quickCheck $ \ (xs :: [Int]) -> ana (\ xs -> if null xs then Left () else Right (head xs, tail xs)) xs == xs




fmap_sum :: forall a b u v. (Functor (WrapFunctor u), Functor (WrapFunctor v)) =>
         (a -> b) -> WrapFunctor (u :+: v) a -> WrapFunctor (u :+: v) b
fmap_sum f (WrapFunctor (Left x)) = WrapFunctor $ Left $ z
    where fmap'' :: WrapFunctor u a -> WrapFunctor u b
          fmap'' = fmap f
          y :: WrapFunctor u a
          y = WrapFunctor x
          z :: Eval u b
          WrapFunctor z = fmap'' y

fmap_sum f (WrapFunctor (Right x)) = WrapFunctor $ Right $ z
    where fmap'' :: WrapFunctor v a -> WrapFunctor v b
          fmap'' = fmap f
          y :: WrapFunctor v a
          y = WrapFunctor x
          z :: Eval v b
          WrapFunctor z = fmap'' y

fmap_prod :: forall a b u v. (Functor (WrapFunctor u), Functor (WrapFunctor v)) =>
         (a -> b) -> WrapFunctor (u :*: v) a -> WrapFunctor (u :*: v) b
fmap_prod f (WrapFunctor (x,y)) = WrapFunctor $ (z,w)
    where fmap' :: WrapFunctor u a -> WrapFunctor u b
          fmap' = fmap f
          fmap'' :: WrapFunctor v a -> WrapFunctor v b
          fmap'' = fmap f
          x' :: WrapFunctor u a
          x' = WrapFunctor x
          y' :: WrapFunctor v a
          y' = WrapFunctor y
          z :: Eval u b
          WrapFunctor z = fmap' x'
          w :: Eval v b
          WrapFunctor w = fmap'' y'

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3