【问题标题】:Type families - cannot derive Base Int?类型族 - 不能派生 Base Int?
【发布时间】:2013-08-31 14:13:40
【问题描述】:

这个想法是实现“惰性”长度函数来将列表长度与 Int 进行比较而不计算整个长度。

{-# LANGUAGE DeriveFunctor
           , TypeFamilies
           , FlexibleInstances #-}
import Data.Functor.Foldable

type instance Base Int   = Maybe

现在我们可以拥有可折叠/不可折叠

instance Foldable Int where
  project 0 = Nothing
  project x = Just (x-1)

instance Unfoldable Int where
  embed Nothing  = 0
  embed (Just x) = x+1

我想将 [a] 转换为 Base Int Int:

leng :: [a] -> Base Int Int
leng = ana phi where
  phi :: [a] -> Base Int [a]
  phi []    = Nothing
  phi (_:t) = Just t

但这不起作用。它抱怨 [a] -> Base (Maybe Int) [a] 应该是 phi 的类型。我不明白为什么。

如果可行,那么我可以比较:

gt = curry $ hylo psi phi where
  phi (Just _, Nothing)  = Left True
  phi (Nothing, _)       = Left False
  phi (Just t, Just n)   = Right (t, n)

  psi (Left t)  = t
  psi (Right t) = t

main = print $ (leng [1..]) `gt` (ana project 4)

len 有什么问题?

【问题讨论】:

    标签: haskell types type-families


    【解决方案1】:

    ana 的类型是(a -> Base t a) -> a -> t。请注意,它返回普通的t 而不是Base t t。所以leng 的正确类型是

    leng :: [a] -> Int
    

    【讨论】:

    • 我真笨!键入 instance Base ([a], Int) = Either Bool,然后以明显的方式定义 Foldable ([a],Int),然后 gt = curry $ cata phi where phi (Left t) = t etc....
    【解决方案2】:

    这可能会破坏使用类型族执行此操作的目的,但如果您只是想要一个“懒惰地比较列表长度与 int”函数,您可以直接编写它:

    cmp :: [a] -> Int -> Ordering
    cmp [] n = compare 0 n
    cmp (_:xs) n = if n <= 0 then GT else cmp xs (n - 1)
    

    【讨论】:

      【解决方案3】:

      @PaulVisschers 正在实现的相同功能,这确实是做你想做的最简单的方法之一,可以用 catamorphism 来实现,如果出于某种原因你想用Foldables 进行练习。

      import Data.Functor.Foldable
      
      cmp :: [a] -> Int -> Ordering
      cmp = cata psi
      
      psi :: Base [a] (Int -> Ordering) -> Int -> Ordering
      psi Nil n = compare 0 n
      psi (Cons h t) n = if n <= 0 then GT else t (n-1)
      

      【讨论】:

      • 我觉得使用高阶函数在某种程度上违背了练习的目的,即理解什么样的态射惰性长度。在我看来,你对 t 所做的事情就像是在模拟具有变质的变形。
      • 无意冒犯,我不是变形(或变形)类型... :-) 我肯定更喜欢 Paul 更简单的代码。我的建议(如果我们忽略范畴论中的练习)唯一可能有用的是,如果您想要使用 fold 的(更高阶)解决方案,这正是您所需要的。那么,这个t 就是你的累加器。
      • 无意冒犯,但 Paul 的代码对于这个目的来说太复杂了。在这里我会更好cmp xs n = (`compare` n) $ length $ take (n+1) xs - 长度的渴望是必要的,而采取的懒惰就足够了。练习的重点是在递归方案上贴上标签。就像在 Java 之后,您发现循环不仅仅是 for 和 while,有时您会发现比 Data.List 中的循环和递归方案更多基本的循环和递归方案
      【解决方案4】:

      感谢您指出类型错误。获得正确的类型澄清了这个想法:)

      {-# LANGUAGE DeriveFunctor
                 , TypeFamilies
                 , FlexibleInstances #-}
      import Data.Functor.Foldable
      
      type instance Base ([a], Int) = Either Bool
      
      instance Foldable ([a], Int) where
        project ([], _) = Left False
        project (_, 0) = Left True
        project ((h:t), n) = Right (t, n-1)
      
      longerThan :: [a] -> Int -> Bool
      longerThan = curry $ cata $ either id id
      
      main = print $ [1..] `longerThan` 4
      

      满意吗?让我们扩展它以说明我为什么真正开始这一切:

      {-# LANGUAGE DeriveFunctor
                 , TypeFamilies
                 , FlexibleInstances
                 , FlexibleContexts
                 , UndecidableInstances #-}
      import Data.Functor.Foldable
      
      data Zip a b x = Z (Base a (Base b x))
      instance (Functor (Base a), Functor (Base b)) => Functor (Zip a b) where
        fmap f (Z a) = Z $ fmap (\x -> fmap f x) a
      
      type instance Base (a, b) = Zip a b
      

      得到提示?我们可以同时递归这两种结构!

      instance (Foldable a, Foldable b) => Foldable (a, b) where
        project (a, b) = Z $ fmap (\x -> fmap (\y -> (x,y)) $ project b) $ project a
      

      演示:引入Base Int,并检查列表的长度是否大于给定的Int。

      type instance Base Int = Maybe
      
      instance Foldable Int where
        project 0 = Nothing
        project x = Just $ x-1
      
      -- lt and gt are the same;
      -- just showing off with the order of arguments, so you can appreciate Zip
      lt :: Int -> [a] -> Bool
      lt = curry $ cata phi where
        phi (Z Nothing) = True
        phi (Z (Just Nil)) = False
        phi (Z (Just (Cons _ t))) = t
      
      gt :: [a] -> Int -> Bool
      gt = curry $ cata phi where
        phi (Z (Cons _ Nothing)) = True
        phi (Z Nil) = False
        phi (Z (Cons _ (Just t))) = t
      
      main = print [[1..] `gt` 4, 4 `lt` [1..]]
      

      【讨论】:

      • 是的,我想使用 Nat,但我们没有,是吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-04
      • 2020-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多