有没有办法使用类型级别模式匹配而不是定义类型系列

时间:2017-10-05 15:09:24

标签: haskell types

考虑以下3个Haskell文件

HList.hs

{-# LANGUAGE DataKinds     #-}
{-# LANGUAGE GADTs         #-}
{-# LANGUAGE PolyKinds     #-}
{-# LANGUAGE TypeOperators #-}
module HList where

data HList :: (k -> *) -> [k] -> * where
  Nil :: HList f '[]
  (:&) :: !(f x) -> HList f xr -> HList f (x ': xr)

Snd.hs

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE TypeOperators       #-}
module Snd where

import           HList

hmap :: forall f g xs. (forall x. f x -> g x) -> HList f xs -> HList g xs
hmap f = go

  where
    go :: HList f xs' -> HList g xs'
    go Nil       = Nil
    go (x :& xs) = f x :& go xs

type family Snd x where
  Snd '(a, b) = b

type family MapSnd xs where
  MapSnd '[] = '[]
  MapSnd (y ': ys) = Snd y ': MapSnd ys

hmap2 :: forall f g (xs :: [(*,*)]). (forall x. f x -> g (Snd x)) -> HList f xs -> HList g (MapSnd xs)
hmap2 f = go
  where
    go :: HList f xs' -> HList g (MapSnd xs')
    go Nil       = Nil
    go (x :& xs) = f x :& go xs

NoSnd.hs

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE TypeOperators       #-}
module NoSnd where

import           HList

hmap :: forall f g xs. (forall x. f x -> g x) -> HList f xs -> HList g xs
hmap f = go
  where
    go :: HList f xs' -> HList g xs'
    go Nil       = Nil
    go (x :& xs) = f x :& go xs

type family MapSnd xs where
  MapSnd '[] = '[]
  MapSnd ('(_, y) ': ys) = y ': MapSnd ys

hmap2 :: forall f g (xs :: [(*,*)]). (forall x a b. (x ~ '(a, b)) => f x -> g b) -> HList f xs -> HList g (MapSnd xs)
hmap2 f = go
  where
    go :: HList f xs' -> HList g (MapSnd xs')
    go Nil       = Nil
    go (x :& xs) = f x :& go xs

编译Snd.hs有效,但编译NoSnd.hs会给出

NoSnd.hs:27:20: error:
    • Could not deduce: x ~ '(a0, x0) arising from a use of ‘f’
      from the context: xs' ~ (x : xr)
        bound by a pattern with constructor:
                   :& :: forall k (a :: k -> *) (x :: k) (xr :: [k]).
                         a x -> HList a xr -> HList a (x : xr),
                 in an equation for ‘go’
        at NoSnd.hs:27:9-15
      ‘x’ is a rigid type variable bound by
        a pattern with constructor:
          :& :: forall k (a :: k -> *) (x :: k) (xr :: [k]).
                a x -> HList a xr -> HList a (x : xr),
        in an equation for ‘go’
        at NoSnd.hs:27:9
    • In the first argument of ‘(:&)’, namely ‘f x’
      In the expression: f x :& go xs
      In an equation for ‘go’: go (x :& xs) = f x :& go xs
    • Relevant bindings include x :: f x (bound at NoSnd.hs:27:9)

Snd.hsNoSnd.hs之间的区别在于,在后者中,我尝试直接解构类型,而不是定义类型族Snd。这发生在两个地方:MapSnd的定义和({1}}的参数类型。

这两个问题是:有人可以解释类型错误;有没有办法在不定义hmap2类型系列的情况下编写hmap2

谢谢。

1 个答案:

答案 0 :(得分:4)

声明

exists a. x ~ '(a, b)

略强于声明

b ~ Snd x

例如,给定

type family Any :: (*, *) where {}

结束

是有效的
Snd Any ~ Snd Any

但结论

无效
exists a. Any ~ '(a, Snd Any)

因为Any并未与其他任何内容统一。

您有HList个按类型(*,*)索引的内容,但您不知道每个元素实际上都有'(a,b)形式的类型,所以你不能将证明证明传递给你给予的功能。现在,如果您有一个不太多态的列表,其中元素类型以已知方式编入索引,那么您可以对元素进行模式匹配以获得所需内容。但这不是你所拥有的,而且它不那么通用和有用。使用当前(工作)公式,传递的函数负责计算它需要了解的有关索引结构的任何信息。但那完全没问题;它比你更了解f的结构。拿你的奖金然后去!