Question

Under what circumstances can a function f :: a -> b -> c -> d be defined by

f w x y z = ...

I would not have thought this possible, but when looking into the enumerator package I found:

enumFileRange :: FilePath
              -> Maybe Integer -- ^ Offset
              -> Maybe Integer -- ^ Maximum count
              -> Enumerator B.ByteString IO b
enumFileRange path offset count step = do
    h <- tryIO (IO.openBinaryFile path IO.ReadMode)
    let iter = enumHandleRange 4096 offset count h step
    Iteratee (Exc.finally (runIteratee iter) (IO.hClose h))

Source

Apparently we have a function of three arguments which is implemented by passing in four arguments. Similarly, the signature of enumHandleRange is

enumHandleRange :: MonadIO m
                => Integer -- ^ Buffer size
                -> Maybe Integer -- ^ Offset
                -> Maybe Integer -- ^ Maximum count
                -> IO.Handle
                -> Enumerator B.ByteString m b

indicating that it has four arguments, yet we invoke it in enumFileRange above by passing in five arguments: let iter = enumHandleRange 4096 offset count h step.

Does someone know how, and in what generality, this works?

Was it helpful?

Solution

Enumerator is a type synonym (which is similar to a typedef in C) for a function type:

type Enumerator a m b = Step a m b -> Iteratee a m b

So enumFileRange actually has the following type:

enumFileRange :: FilePath
              -> Maybe Integer -- ^ Offset
              -> Maybe Integer -- ^ Maximum count
              -> Step B.ByteString IO b
              -> Iteratee B.ByteString IO b

OTHER TIPS

All functions in Haskell have a signature of the form a -> b. If the function has "more than one parameter", this merely means that b happens to be a function type again. So, to answer

Under what circumstances can a function f :: a -> b -> c -> d be defined by

f w x y z = ...

the answer would be always, because unconstrained type variables like a, b, c, d can always be resolved to a function type. In fact, even with type class constraints that are "obviously incompatible" with being functions, the type checker will initially assume this, which is why we sometimes get so funny error messages like

GHCi, version 7.6.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> 7 "x"

<‌interactive‌>:2:1:
    No instance for (Num ([Char] -> t0)) arising from the literal `7'
    Possible fix: add an instance declaration for (Num ([Char] -> t0))
    In the expression: 7
    In the expression: 7 "x"
    In an equation for `it': it = 7 "x"

For fixed types such as Enumerator B.ByteString IO b this is not true. In this case, is it really necessary that you're dealing with a typedef of a function type, which like rightfold said is the case for Enumerator.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top