我想代表(before, during, after)
对的列表,使得all (\(_, _, after), (before, _, _) -> after == before) $ zip xs (tail xs)
;换句话说,使得相邻的对在其末端匹配。最终目标是提取(before, [(during, after)]
的折叠列表。
我们可以使用类似Star
的结构来强制匹配,从而轻松实现这一目标:
{-# LANGUAGE GADTs, PolyKinds, DataKinds, KindSignatures, TypeApplications, ScopedTypeVariables #-}
import Data.Singletons
data Step a (before :: ending) (after :: ending) where
Step :: (SingI before, SingI after) => a -> Step a before after
infixr 5 :>
data Steps a (first :: ending) where
Nil :: Steps a first
(:>) :: Step a before after -> Steps a after -> Steps a before
before :: forall ending a (before :: ending) (after :: ending). (SingKind ending) => Step a before after -> Demote ending
before Step{} = FromSing (sing @before)
after :: forall ending a (before :: ending) (after :: ending). (SingKind ending) => Step a before after -> Demote ending
after Step{} = FromSing (sing @after)
collapse :: forall ending a (first :: ending). (SingKind ending) => Steps a first -> (Maybe (Demote ending), [(a, Demote ending)])
collapse Nil = (Nothing, [])
collapse xs@(x :> _) = (Just $ before x, go xs)
where
go :: Steps a whatever -> [(a, Demote ending)]
go Nil = []
go (s@(Step x) :> xs) = (x, after s) : go xs
用法示例:
{-# LANGUAGE TemplateHaskell, PatternSynonyms, TypeFamilies, StandaloneDeriving #-}
import Data.Singletons.TH
$(singletons [d|
data Ending = A | B | C
|])
deriving instance Show Ending
foo :: Steps Int A
foo =
Step @A @B 1 :>
Step @B @C 2 :>
Nil
> collapse foo (Just A,[(1,B),(2,C)])
到目前为止很好。
但是,现在让我们说其中一些Step
并不真的在乎它们的结尾。虽然我们可以让类型推断将它们填入明确的位置,例如
example1 :: Steps Int A
example1 =
Step 1 :>
Step @B @C 2 :>
Nil
但是,如果没有足够的约束来解决歧义,例如:失败了:
example2 :: Steps Int A
example2 =
Step 1 :>
Step 2 :>
Step @B @C 3 :>
Nil
使用
after0
引起的模棱两可的类型变量Step
阻止约束(SingI after0)
被解决。
这是可以理解的,这是可以理解的。例如,没有地方可以拉到ending
d列表中的collapse
。
相反,我们可以通过在两侧使用Maybe ending
来使“不关心”明确:
data Step a (before :: Maybe ending) (after :: Maybe ending) where
Step :: (SingI before, SingI after) => a -> Step a before after
data Steps a (first :: Maybe ending) where
Nil :: Steps a first
(:>) :: Step a before after -> Steps a after -> Steps a before
infixr 5 :>
before :: forall ending a (before :: Maybe ending) (after :: Maybe ending). (SingKind ending) => Step a before after -> Maybe (Demote ending)
before Step{} = FromSing (sing @before)
after :: forall ending a (before :: Maybe ending) (after :: Maybe ending). (SingKind ending) => Step a before after -> Maybe (Demote ending)
after Step{} = FromSing (sing @after)
collapse :: forall ending a (first :: Maybe ending) . (SingKind ending) => Steps a first -> (Maybe (Demote ending), [(a, Maybe (Demote ending))])
collapse Nil = (Nothing, [])
collapse xs@(x :> _) = (before x, go xs)
where
go :: Steps a whatever -> [(a, Maybe (Demote ending))]
go Nil = []
go (s@(Step x) :> xs) = (x, after s) : go xs
然后显式地建模Nothing
和Just ending
之间的关系,并在必要时减弱:
type family Meet (a :: Maybe k) (b :: Maybe k) where
Meet Nothing b = b
Meet a Nothing = a
Meet (Just a) (Just a) = Just a
infixr 5 >:>
(>:>) :: forall before after after' meet a. (Meet after after' ~ meet, SingI meet) => Step a before after -> Steps a after' -> Steps a before
Step x >:> xs = Step x :> xs'
where
xs' :: Steps a meet
xs' = case xs of
Nil -> Nil
Step y :> ys -> Step y :> ys
这使我们可以使用example2
来表示结尾的Nothing
没有任何限制:
example2 :: Steps Int (Just A)
example2 =
Step @(Just A) @Nothing 1 >:>
Step @Nothing @Nothing 2 >:>
Step @(Just B) @(Just C) 3 >:>
nil
> collapse example2 (Just A,[(1,Nothing),(2,Just B),(3,Just C)])
所以现在我的问题
是否可以消除对显式@Nothing
类型应用程序的需求?也就是说,是否可以添加一些基础结构,使其与上面的example2
相同:
example2' :: Steps Int (Just A)
example2' =
Step @(Just A) 1 >:>
Step 2 >:>
Step @(Just B) @(Just C) 3 >:>
nil
?