默认中间类型变量

时间:2019-07-08 09:42:24

标签: haskell type-inference type-level-computation data-kinds singleton-type

我想代表(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

然后显式地建模NothingJust 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

0 个答案:

没有答案