Haskell:函数作为数据类型

时间:2018-03-05 18:24:15

标签: haskell

我觉得学习haskell的一个思维障碍是data有时将函数定义为数据。

data Person = Person {
  name :: String,
  age  :: Int
}

这很直观,类似于其他语言。但是在

newtype StateT s m a = StateT {
  runStateT :: s -> m (a,s)
}

这基本上是调用函数s->m (a,s)“data”

我很容易理解,在高阶函数中,“函数”确实作为数据传递。但是在类型定义中,使用函数来定义类型,这是非常令人惊讶的。

所以我的问题是:这会给Haskell类型系统带来表现力吗?这一切背后的理论是什么?

2 个答案:

答案 0 :(得分:7)

它只是一个围绕函数的包装器。

foo :: String -> [(Int, String)]
foo xs = zip [1..] (map pure xs)

fooState :: StateT String [] Int
fooState = StateT foo

数据构造函数StateT采用单个参数,类型为s -> m (a, s)的函数,并返回类型StateT s m a的值。在这里,我们有

  • s ~ String
  • m ~ []
  • a ~ Int

由于声明类型为foo

与Python之类的语言中的函数引用真的没什么不同。 (请注意,foo的类型与Haskell示例略有不同,但将引用传递给fooStateT.__init__的想法是重要的注意)。

class StateT:
   def __init__(self, f):
       self.runStateT = f

def foo(xs):
    return enumerate(xs)

x = StateT(foo)

答案 1 :(得分:2)

与任何其他值一样,函数具有类型,并且如您所说,可以作为参数传递。数据类型字段也可以存储函数,这与其他值的使用方式没有区别:

GHCi> :t ("foo", "bar")
("foo", "bar") :: ([Char], [Char])
GHCi> :t (reverse, drop 2)
(reverse, drop 2) :: ([a1] -> [a1], [a2] -> [a2])

从这个角度来看,......之间没有本质区别。

newtype MyInt = MyInt { getMyInt :: Int }

......和:

newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
  

这会给Haskell类型系统带来表现力吗?

以下是两种方式。首先,函数类型的包装器和类型同义词允许编写较少杂乱的类型签名。这个,例如......

withStateT :: (s -> s) -> StateT s m a -> StateT s m a

......读起来比以前好多了:

withStateT :: (s -> s) -> (s -> m (a, s)) -> (s -> n (a, s))

其次,newtype包装器使得为函数类型编写类实例变得可行 - 没有它们,例如,我们不会拥有StateT具有的重要实例(FunctorApplicativeMonadMonadTrans等。)