在Haskell中,如果我想重复将内同性a -> a
应用于a
类型的值,则可以只使用iterate
。
不是内同态但足够通用以在其返回类型上正常工作的函数呢?
考虑例如Just :: a -> Maybe a
;我会写
Just . Just . Just ...
我要多少次有没有一种方法可以像这样写
iterate' 3 Just :: a -> Maybe (Maybe (Maybe a))
还是我们需要像依赖类型这样的东西?
最佳答案
可能需要稍微修改一下您建议的语法:iterate' @3 Just
而不是iterate' 3 Just
。
这是因为结果类型取决于数字,所以数字必须是类型文字,而不是值文字。正如您正确指出的那样,使用任意数字执行此操作将需要Haskell没有的依赖类型[1]。
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE TypeFamilies, KindSignatures, DataKinds,
FlexibleInstances, UndecidableInstances, ScopedTypeVariables,
FunctionalDependencies, TypeApplications, RankNTypes, FlexibleContexts,
AllowAmbiguousTypes #-}
import qualified GHC.TypeLits as Lit
-- from type-natural
import Data.Type.Natural
import Data.Type.Natural.Builtin
class Iterate (n :: Nat) (f :: * -> *) (a :: *) (r :: *)
| n f a -> r
where
iterate_peano :: Sing n -> (forall b . b -> f b) -> a -> r
instance Iterate 'Z f a a where
iterate_peano SZ _ = id
instance Iterate n f (f a) r => Iterate ('S n) f a r where
iterate_peano (SS n) f x = iterate_peano n f (f x)
iterate'
:: forall (n :: Lit.Nat) f a r .
(Iterate (ToPeano n) f a r, SingI n)
=> (forall b . b -> f b) -> a -> r
iterate' f a = iterate_peano (sToPeano (sing :: Sing n)) f a
如果您将其加载到ghci中,可以说
*Main> :t iterate' @3 Just
iterate' @3 Just :: a -> Maybe (Maybe (Maybe a))
*Main> iterate' @3 Just True
Just (Just (Just True))
该代码使用两种不同的类型级自然语言:来自
Nat
的内置GHC.TypeLits
和来自Data.Type.Natural
的经典Peano数字。前者需要提供漂亮的iterate' @3
语法,后者需要执行递归(这发生在Iterate
类中)。我使用Data.Type.Natural.Builtin
从文字转换为相应的Peano数字。[1]但是,给定一种消耗迭代值的特定方式(例如,如果您事先知道只想对它们进行
show
编码),则即使对于n
的动态值,也可以使此代码适用。 iterate'
类型没有什么需要静态已知的Nat
;唯一的挑战是证明迭代结果满足您所需的约束。