简体   繁体   中英

Haskell: Function as Data type

I feel one of the mind hurdles in learning haskell is that data sometimes defines functions as data.

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

This is intuitive and resembles other languages. But in

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

This is basically calling a function s->m (a,s) "data"

I can readily understand that in higher order functions, "functions" are indeed passed around as data. But in type definitions, using functions to define types, that's quite surprising.

So my question is: will this bring expressiveness to Haskell type system? What is the theory behind all this?

It's just a wrapper around a function.

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

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

The data constructor StateT takes a single argument, a function of type s -> m (a, s) , and returns a value of type StateT sma . Here, we have

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

due to the declare type of foo .


It's really not very different from a function reference in a language like Python. (Note that foo has a slightly different type here than in the Haskell example, but the idea of passing a reference to foo to StateT.__init__ is the important thing to note).

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

def foo(xs):
    return enumerate(xs)

x = StateT(foo)

Functions, like any other value, have types and, as you say, can be passed as arguments. Fields of data types can also store functions, which again is no different from how other values can be used:

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

From this point of view, there is no essential difference between...

newtype MyInt = MyInt { getMyInt :: Int }

... and:

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

will this bring expressiveness to Haskell type system?

Here are two of the ways in which it does so. Firstly, wrappers and type synonyms for function types allow writing less cluttered type signatures. This, for example...

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

... reads quite a bit nicer than:

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

Secondly, newtype wrappers make it feasible to write class instances for function types -- without them, for instance, we wouldn't have the crucially important instances that StateT has ( Functor , Applicative , Monad , MonadTrans , etc.).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM