Excluding Types in Haskell - haskell

How would I use pattern matching in order to exclude certain types of inputs? For instance given the following:
f list k =
if null list
then []
else head list + k : f (tail list) k
How can I use pattern matching to make f s/t it only allows Int and Integer but not Double or Float?

As suchtgott explained, you should not actually try to limit your function to exactly Int and Integer; you probably want to use an Integral constraint.
Suppose, for fun, that you really did want to limit it just like that. The way to do this in Haskell 98 is a bit weird (jump down to the break to see how this is done in GHC Haskell:
class Integral a => IntOrInteger a where
intOrInteger :: Either (f a -> f Int) (f a -> f Integer)
instance IntOrInteger Int where
intOrInteger = Left id
instance IntOrInteger Integer where
intOrInteger = Right id
How do you use such a thing? Well, to start off with
intOrIntegerSimple :: IntOrInteger a => Either (a -> Int) (a -> Integer)
intOrIntegerSimple = case intOrInteger of
Left f -> Left (runIdentity . f . Identity)
Right f -> Right (runIdentity . f . Identity)
But how do you flip it around, and turn an Int or Integer back to an instance of IntOrInteger?
newtype Switch f a i = Switch {runSwitch :: f i -> f a}
intOrInteger' :: IntOrInteger a => Either (f Int -> f a) (f Integer -> f a)
intOrInteger' = case intOrInteger of
Left f -> Left (runSwitch (f (Switch id)))
Right f -> Right (runSwitch (f (Switch id)))
intOrIntegerSimple' :: IntOrInteger a => Either (Int -> a) (Integer -> a)
intOrIntegerSimple' = case intOrInteger' of
Left f -> Left (runIdentity . f . Identity)
Right f -> Right (runIdentity . f . Identity)
When you don't really care whether you have an Int or an Integer, but want to be sure you really have one of them, you have to watch out for invalid instances. What might an invalid instance look like? This is one option:
instance IntOrInteger Word where
intOrInteger = Left (const undefined)
To just test that the instance is valid, you can use this (importing Data.Proxy):
ensureIntOrInteger :: IntOrInteger a => Proxy a -> ()
ensureIntOrInteger p = case intOrInteger of
Left f -> f p `seq` ()
Right f -> f p `seq` ()
The result of ensureIntOrInteger will be defined if and only if the type a is actually an Int or an Integer.
Nice; it works. But in practice it's pretty nasty to use. You can do much better with a few GHC extensions:
{-# LANGUAGE GADTs, TypeFamilies, TypeOperators, ConstraintKinds,
UndecidableInstances, UndecidableSuperClasses, DataKinds #-}
-- In 8.0 and later, Constraint is also available from Data.Kind
import GHC.Exts (Constraint)
import Data.Type.Equality ((:~:)(..))
import GHC.TypeLits (TypeError, ErrorMessage (..))
type family IntOrIntegerC a :: Constraint where
IntOrIntegerC Int = ()
IntOrIntegerC Integer = ()
IntOrIntegerC t = TypeError ('ShowType t :<>:
'Text " is not an Int or an Integer.")
class (Integral a, IntOrIntegerC a) => IntOrInteger a where
intOrInteger :: Either (a :~: Int) (a :~: Integer)
instance IntOrInteger Int where
intOrInteger = Left Refl
instance IntOrInteger Integer where
intOrInteger = Right Refl
With this formulation, the IntOrIntegerC constraint family blocks out any invalid types without your needing to do anything, giving a useful error message if someone tries to write a bogus instance. And if you actually do need to use the equality evidence, it's simply a matter of pattern matching on intOrInteger or using the various handy functions in Data.Type.Equality.
A point of style: using head and tail is generally discouraged in Haskell. We prefer to pattern match instead. Your function could be written
f [] _ = []
f (x : xs) k = x + k : f xs k

f/mapAdd implements a map. map is
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x : xs) = f x : map f xs
and can be used to formulate mapAdd:
Prelude> mapAdd n = map (+ n)
Prelude> mapAdd 3 [1,2,3,4]
[4,5,6,7]
GHCi's :type command tells you the automatically inferred type signature of mapAdd:
Prelude> :type mapAdd
mapAdd :: Num b => b -> [b] -> [b]
Here, mapAdd's bs are constrained to the general numerical type class Num. You can explicitly constrain to the type class Integral, which (basically) exclusively contains the data types Int, and Integer. Integral is a subclass of Num.
Prelude> :{
Prelude| mapAdd :: Integral a => a -> [a] -> [a]
Prelude| mapAdd n = map (+ n)
Prelude| :}
Prelude> :t mapAdd
mapAdd :: Integral a => a -> [a] -> [a]
mapAdd n = map (+ n) is pointfree for mapAdd n lst = map (+ n) lst.

You can create a dumb type class with instances only for Int and Integer and then use a type constraint:
class (Num a) => IntOrInteger a
instance IntOrInteger Int
instance IntOrInteger Integer
f :: (IntOrInteger a) => [a] -> a -> [a]
f list k =
if null list
then []
else head list + k : f (tail list) k
Then if you use f with Int, it will compile:
> f [1,2,3] 1
[2,3,4]
But it will not compile with Double:
> f [1,2,3] (1::Double)
<interactive>:19:1: error:
* No instance for (IntOrInteger Double) arising from a use of `f'
* In the expression: f [1, 2, 3] (1 :: Double)
In an equation for `it': it = f [1, 2, 3] (1 :: Double)

There are a lot of ways to constrain types, the most direct is just with type families.
type family Elem x xs :: Constraint where
Elem x (x ': xs) = ()
Elem x (y ': xs) = Elem x xs
Elem x '[] = TypeError ('ShowType x :<>: 'Text " is not a permitted type.")
type a ~~ b = Elem a b
function :: (a ~~ [Int, Integer], Num a) => [a] -> [a]
This allows you to white list a set of types. You won't know which one in particular you have though, that's more complicated.
However, you said you wanted to exclude certain types, so perhaps you want a black list. We simply flip the first and last case:
type family NotElem x xs :: Constraint where
NotElem x (x ': xs) = TypeError ('ShowType x :<>: 'Text " is a forbidden type.")
NotElem x (y ': xs) = NotElem x xs
NotElem x '[] = ()
type a !~~ b = NotElem a b
function :: (a !~~ [Double, Float], Num a) => [a] -> [a]
function will now accept any Num type that is not a Double or Float.
However, there's no reason to actually do any of this for your example function. You lose nothing at all by allowing it to work on the full range of Num types. In fact, these hijinks will at the least cost you a tiny smidgen more time on type checking, or if you go further and do reflection based things, possibly run time overhead as well.

Related

Type level computations on types including type variables

I was working recently with freer and I got inspired to try creating a method that allows composing of arbitrary arity functions using type level computations, for example:
(+) :: Integer -> Integer -> Integer
x + y = ...
(>) :: Integer -> Integer -> Bool
x > y = ...
(sumGtThan5) :: Integer -> Integer -> Bool
sumGtThan5 x y = (+) ..> (>5)
I got that working for concrete types of functions, for example, following code compiles and allows composing functions.
-- source code
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE ImpredicativeTypes #-}
module Control.Pointfree where
import Data.Kind (Type)
-- | Manipulating function types
type family TypeLevelFunctionToParams fun :: [Type] where
TypeLevelFunctionToParams (a -> b -> c -> d) = '[a, b, c, d]
TypeLevelFunctionToParams (a -> b -> c) = '[a, b, c]
TypeLevelFunctionToParams (a -> b) = '[a, b]
type family TypeLevelParamsToFunction params :: * where
TypeLevelParamsToFunction '[a, b, c, d] = (a -> b -> c -> d)
TypeLevelParamsToFunction '[a, b, c] = (a -> b -> c)
TypeLevelParamsToFunction '[a, b] = (a -> b)
-- | Type level lists algebra
-- | Not handling empty type level lists for now. What happens if you pass one here? I don't know. Probably bad things.
type Head :: forall a. [a] -> a
type family Head xs where
Head (x:xs) = x
type Tail :: forall a. [a] -> [a]
type family Tail xs where
Tail (x:xs) = xs
type Last :: forall a. [a] -> a
type family Last xs where
Last (x : '[]) = x
Last (x:xs) = Last xs
type Init :: forall a. [a] -> [a]
type family Init xs where
Init (x : '[]) = '[]
Init (x:xs) = x ': Init xs
type Cons :: forall a. a -> [a] -> [a]
type family Cons x xs where
Cons c xs = c ': xs
type Snoc :: forall a. a -> [a] -> [a]
type family Snoc x xs where
Snoc x '[] = '[x]
Snoc s (x:xs) = x ': Snoc s xs
-- | Composing various arity functions
type Result f = Last (TypeLevelFunctionToParams f)
type Args f = Init (TypeLevelFunctionToParams f)
type FunctionWithNewResult res f = TypeLevelParamsToFunction (Snoc res (Args f))
class Composable (f :: *) where
(..>) :: forall res2. f -> (Result f -> res2) -> FunctionWithNewResult res2 f
instance Composable (Integer -> Bool) where
f ..> g = g . f
instance Composable (Integer -> Integer -> Integer) where
f ..> g = \x y -> g (f x y)
-- instance {-# OVERLAPPABLE #-} Composable (a -> b) where
-- f ..> g = g . f
-- GHCI session
ghci> import Control.Pointfree
ghci> :set -XDataKinds
ghci> :set -XPolyKinds
ghci> :set -XRankNTypes
ghci> :set -XFlexibleContexts
ghci> :set -XScopedTypeVariables
ghci> sumGtThan5 :: Integer -> Integer -> Bool = (+) ..> (>5)
ghci> sumGtThan5 2 1
False
ghci> sumGtThan5 7 3
True
However, uncommenting a Composable (a -> b) instance triggers a following compilation error:
src\Control\Pointfree.hs:72:13: error:
* Couldn't match type `b'
with `Last (TypeLevelFunctionToParams (a -> b))'
Expected: b -> res2
Actual: Result (a -> b) -> res2
`b' is a rigid type variable bound by
the instance declaration
at src\Control\Pointfree.hs:71:31-49
* In the first argument of `(.)', namely `g'
In the expression: g . f
In an equation for `..>': f ..> g = g . f
* Relevant bindings include
g :: Result (a -> b) -> res2
(bound at src\Control\Pointfree.hs:72:9)
f :: a -> b (bound at src\Control\Pointfree.hs:72:3)
(..>) :: (a -> b)
-> (Result (a -> b) -> res2) -> FunctionWithNewResult res2 (a -> b)
(bound at src\Control\Pointfree.hs:72:5)
|
72 | f ..> g = g . f
| ^
src\Control\Pointfree.hs:72:13: error:
* Couldn't match type: TypeLevelParamsToFunction
(Snoc res2 (Init (TypeLevelFunctionToParams (a -> b))))
with: a -> res2
Expected: FunctionWithNewResult res2 (a -> b)
Actual: a -> res2
* In the expression: g . f
In an equation for `..>': f ..> g = g . f
In the instance declaration for `Composable (a -> b)'
* Relevant bindings include
g :: Result (a -> b) -> res2
(bound at src\Control\Pointfree.hs:72:9)
f :: a -> b (bound at src\Control\Pointfree.hs:72:3)
(..>) :: (a -> b)
-> (Result (a -> b) -> res2) -> FunctionWithNewResult res2 (a -> b)
(bound at src\Control\Pointfree.hs:72:5)
|
72 | f ..> g = g . f
I have experimented with GHCI and I think I found the root of the problem - I think that GHC doesn't reduce type level operations on types that contain type variables.
GHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Control.Pointfree ( src\Control\Pointfree.hs, interpreted )
Ok, one module loaded.
ghci> import Control.Pointfree
ghci> :set -XDataKinds
ghci> :set -XPolyKinds
ghci> :set -XRankNTypes
ghci> :set -XFlexibleContexts
ghci> :set -XScopedTypeVariables
ghci> :kind! Integer -> Integer
Integer -> Integer :: *
= Integer -> Integer
ghci> :kind! Integer -> Integer -> Bool
Integer -> Integer -> Bool :: *
= Integer -> Integer -> Bool
ghci> :kind! forall a b. a -> b
forall a b. a -> b :: *
= a -> b
ghci> :kind! Init (TypeLevelFunctionToParams (Integer -> Integer))
Init (TypeLevelFunctionToParams (Integer -> Integer)) :: [*]
= '[Integer]
ghci> :kind! Init (TypeLevelFunctionToParams (Integer -> Integer -> Integer))
Init (TypeLevelFunctionToParams (Integer -> Integer -> Integer)) :: [*]
= '[Integer, Integer]
ghci> :kind! forall a b c. Init (TypeLevelFunctionToParams (a -> b -> c))
-- NOT REDUCED!
forall a b c. Init (TypeLevelFunctionToParams (a -> b -> c)) :: [*]
= Init (TypeLevelFunctionToParams (a -> b -> c))
I am wondering why is it so? Is there any documentation about behaviour of type variables in such context? Do you know any workarounds for this problem?
GHC doesn't reduce type level operations on types that contain type variables.
That's not quite the problem. The type family TypeLevelFunctionToParams is defined by multiple clauses, and it's not clear which clause applies, since depending on whether c is a _ -> _ or not, TypeLevelFunctionToParams (a -> b -> c) might reduce using the first or second clause. Clauses in closed type families are ordered, so TypeLevelFunctionToParams (a -> b -> c) = '[a,b,c] only if c is not a function type c1 -> c2.
Defining the "arity" of a function by "counting arrows" is fundamentally ambiguous in that way, and there is no canonical way to deal with it. In your case you might have to supply the arity upfront. You can also look for other examples of "variadic functions in Haskell" for ideas.

Type-level constraints in instances of type families

Is it possible to have type synonym families for parametrized data such as Data.Param.FSVec?
Ideally, I would like this to compile:
class A e where
type Arg e a
f :: (Arg e a -> b) -> e a -> e b
instance A X where
type Arg X a = Nat size => FSVec size a
f = {- implementation -}
I have tried several workarounds, like wrapping FSVec size a in a newtype, or constraint synonyms, but it seems that I could not get anything reasonable right.
Context + minimal working example
A is a class previously defined (for example) as such:
class OldA e where
f :: (Maybe a -> b) -> [e (Maybe a)] -> [e b]
An example of type inheriting OldA is:
data Y a = Y a
instance Functor Y where
fmap f (Y a) = Y (f a)
instance OldA Y where
f = fmap . fmap
I want to extend this class to be able to express more general function arguments for f. Let's say we have a type X and an associated function fIndependent:
import qualified Data.Param.FSVec as V
import Data.TypeLevel hiding ((==))
data X a = X a deriving Show
fromX (X a) = a
fIndependent :: (Nat size) => (V.FSVec size (Maybe a) -> b) -> [X (Maybe a)] -> [X b]
fIndependent _ [] = []
fIndependent f xs = let x' = (V.reallyUnsafeVector . take c . fmap fromX) xs
xs' = drop c xs
c = V.length x'
in if c == length (V.fromVector x') then X (f x') : fIndependent f xs' else []
fIndependent is sane itself. Testing it with a function
test :: V.FSVec D2 x -> Int
test a = V.length a
will grant the result:
>>> fIndependent test $ map (X . Just) [1,2,3,4,5,6,7,8,9]
[X 2, X 2, X 2, X 2]
Ok, now how to extend OldA? The most "natural" thing that came into my mind is to equip class A with a type synonym family Arg e a as below.
class NewA e where
type Arg e a
f :: (Arg e a -> b) -> [e (Maybe a)] -> [e b]
Converting all existing instances is easy:
instance NewA Y where
type Arg Y a = Maybe a
f = fmap . fmap -- old implementation
To express fIndependent as f is the difficult part, since just adding
instance NewA X where
type Arg X a = (Nat size) => FSVec size (Maybe a) -- wrong!!!
f = {- same as fIndependent -}
does not work. This is what I have trouble with.
Try-outs
Most solutions I saw propose wrapping FSVec inside a newtype. Doing so does not help since the following code:
{-# LANGUAGE RankNTypes #-}
newtype ArgV a = ArgV (forall rate.Nat rate => V.FSVec rate (Maybe a))
instance NewA X where
type Arg X a = ArgV a
g f xs = let x' = (V.reallyUnsafeVector . take c . fmap fromX) xs
xs' = drop c xs
c = V.length x'
in if c == length (V.fromVector x') then X (f $ ArgV x') : g f xs' else []
the type inference system seems to lose the information about size:
Couldn't match type ‘s0’ with ‘rate’ …
because type variable ‘rate’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context: Nat rate => V.FSVec rate (Maybe a)
Expected type: V.FSVec rate (Maybe a)
Actual type: V.FSVec s0 (Maybe a)
Relevant bindings include
x' :: V.FSVec s0 (Maybe a)
(bound at ...)
In the first argument of ‘Args’, namely ‘x'’
In the second argument of ‘($)’, namely ‘Args x'’
Compilation failed.
I would appreciate any lead or hint in this matter.
It appears that you are using a class Nat :: k -> Constraint and a data type FSVec :: k -> * -> *. The data type is constrained with the old DatatypeContexts extension.
{-# LANGUAGE DatatypeContexts #-}
class Nat n
data Nat n => FSVec n a = FSVec -- ...
You have an existing class A :: (* -> *) -> Constraint which you'd like to write an FSVec instance for.
class A e where
--- ...
f :: ( {- ... -} b) -> e a -> e b
But FSVec can never have an A instance, because it's a kind mismatch. The class A requires a type argument with the kind * -> * but FSVec has the kind k -> * -> *. You've already run into a problem, and aren't even using the type family yet. If you try to do this (hand waving away what the type family argument is for now)
data X = X
instance A (FSVec) where
type Arg FSVec a = X
f = undefined
You get a compiler error.
Expecting one more argument to `FSVec'
The first argument of `A' should have kind `* -> *',
but `FSVec' has kind `* -> * -> *'
In the instance declaration for `A (FSVec)'
Everything before here, including the compiler error, is useful information for communicating the problem you are having and is useful in asking for help.
Fortunately it's a really easy problem to fix. If you pick some natural number n, then FSVec n has the kind * -> *, which matches the kind of the type argument to A. You can start writing an instance A (FSVec n)
instance A (FSVec n) where
f = -- ...
When you reintroduce the complete class definition with type families
{-# LANGUAGE TypeFamilies #-}
class A e where
type Arg e a
f :: (Arg e a -> b) -> e a -> e b
The solution is still to write an A instance for FSVec n instead of for FSVec. Now that n has moved into the instance declaration, there's an obvious place to capture the needed Nat n context.
instance Nat n => A (FSVec n) where
type Arg (FSVec n) a = FSVec n a
f = undefined -- ...
Cirdec's answer explains one of the problems, but its solution given does not exactly answer the question posted. The question asks for an instance X for the class A, with a FSVec type synonym.
The overarching issue here that prevents defining type Arg X = FSVec size a (in any possible configuration) is that type families are not injective. Knowing this and following Cirdec's reasoning, I can think of a workaround to achieve this goal: include a proxy "context" variable in Xs type, to overcome the mentioned issue.
data X c a = X a
instance (Nat n) => A (X n) where
type (X n) a = FSVec n a
f = {- same as fIndependent -}
Of course, this is a quick fix that works for the minimal example (i.e. it answers the question posted), but might not scale well when composing multiple functions like f since there might appear type clashes between the inferred "contexts".
The best solution I can think of would be to add a constraint synonym (as suggested by this answer) for each instance, like:
import qualified Data.Param.FSVec
import Data.TypeLevel
import GHC.Exts -- for Constraint kind
class A e where
type Arg e context a
type Ctx e context :: Constraint
f :: (Ctx e context) => (Arg e context a -> b) -> [e (Maybe a)] -> [e b]
instance A Y where
type Arg Y c a = Maybe a
type Ctx Y c = ()
f = {- same as before -}
instance A X where
type Arg X size a = V.FSVec size (Maybe a)
type Ctx X size = Nat rate
f = {- same as fIndependent -}
But then we would have to deal with the ambiguous types resulted due to the infamous non-injectivity of type families (e.g. Could not deduce: Arg e context0 a ~ Arg e context a). In this case proving injectivity would have to be done manually using the TypeFamilyDependencies extension (based on injective type families) available in GHC 8.0, and define Arg as:
type family Arg (e :: * -> *) context = (r :: * -> *) | r -> context
Of course, this is not possible if the design of the type family is not injective (which is my case), but it is the cleanest solution so far. It is definitely recommended if one can design her type family using the guidelines in the provided paper.

Traversable for data Constant a b = Constant a passes Quickchecks, but acts funny

The haskell book wants me to implement the traversable instance for
newtype Constant a b = Constant { getConstant :: a }
including all necessary superclasses. The code below passes Quickcheck/Checkers, but acts funny
import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes
newtype Constant a b = Constant { getConstant :: a }
instance Functor (Constant a) where
fmap f (Constant a) = Constant a
instance Foldable (Constant a) where
foldr f z (Constant x) = z
instance Traversable (Constant a) where
traverse f (Constant a) = pure $ Constant a
type TI = []
main = do
let trigger = undefined :: TI (Int, Int, [Int])
quickBatch (traversable trigger)
When I try to use the traversable instance like so:
traverse (\x -> [x + 1]) $ Constant 5
I do not get Constant [5] which I was hoping for, but rather
traverse (\x -> [x + 1]) $ Constant 5
:: (Num b, Num a) => [Constant a b]
What does it mean? Have I done something wrong?
When I try to use the traversable instance like so:
traverse (\x -> [x + 1]) $ Constant 5
I do not get Constant [5] which I was hoping for [...]
You're not going to get Constant [5]. Let's write the type for traverse:
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
...and line it up with your instantiation of it:
-- I've substituted `x` for `a` and `y` for `b` in the
-- first type, because otherwise my head hurts.
(x -> f y) -> t x -> f (t y)
(Num a, Num b) => (a -> [] a) -> (Constant b) a -> [] ((Constant b) a)
So we have:
t = Constant b
f = []
x = Num a => a
y = NUm b => b
Note that the type for traverse implies that t will be the same in the argument and the result. Since you're using Constant 5 :: Num a => Constant a b as the argument, that implies that you can never have Constant [5] :: Num a => Constant [a] b in the result, because Constant a /= Constant [a].

Recreating Lisp's `apply` in Haskell using GADTs

As an exercise I'm trying to recreate Lisp's apply in Haskell. I do not intend to use this for any practical purpose, I just think it's a nice opportunity to get more familiar with Haskell's type system and type systems in general. (So I am also not looking for other people's implementations.)
My idea is the following: I can use GADTs to "tag" a list with the type of the function it can be applied to. So, I redefine Nil and Cons in a similar way that we would encode list length in the type using a Nat definition, but instead of using Peano numbers the length is in a way encoded in the tagging function type (i.e. length corresponds to the number of arguments to the function).
Here is the code I have so far:
{-# LANGUAGE GADTs #-}
-- n represents structure of the function I apply to
-- o represents output type of the function
-- a represents argument type of the function (all arguments same type)
data FList n o a where
-- with Nil the function is the output
Nil :: FList o o a
-- with Cons the corresponding function takes one more argument
Cons :: a -> FList f o a -> FList (a -> f) o a
args0 = Nil :: FList Int Int Int -- will not apply an argument
args1 = Cons 1 args0 -- :: FList (Int -> Int) Int Int
args2 = Cons 2 args1 -- :: FList (Int -> Int -> Int) Int Int
args3 = Cons 3 args2 -- :: FList (Int -> Int -> Int -> Int) Int Int
listApply :: (n -> o) -> FList (n -> o) o a -> o
-- I match on (Cons p Nil) because I always want fun to be a function (n -> o)
listApply fun (Cons p Nil) = fun p
listApply fun (Cons p l) = listApply (fun p) l
main = print $ listApply (+) args2
In the last line, my idea would be that (+) will be of type Int -> Int -> Int, where Int -> Int corresponds to the n in (n -> o) and o corresponds to the last Int (the output) [1]. As far as I can tell, this type seems to work out with the type of my argsN definitions.
However, I get two errors, of which I will state the component that seems relevant to me:
test.hs:19:43:
Could not deduce (f ~ (n0 -> f))
from the context ((n -> o) ~ (a -> f))
bound by a pattern with constructor
Cons :: forall o a f. a -> FList f o a -> FList (a -> f) o a,
in an equation for ‘listApply’
and
test.hs:21:34:
Couldn't match type ‘Int’ with ‘Int -> Int’
Expected type: FList (Int -> Int -> Int) (Int -> Int) Int
Actual type: FList (Int -> Int -> Int) Int Int
In the second argument of ‘listApply’, namely ‘args2’
I'm not sure how to interpret the first error. The second error is confusing me since it does not match with my interpretation stated marked with [1] earlier.
Any insights into what is going wrong?
P.S: I'm more than willing to learn about new extensions if that would make this work.
You got it almost right. Recursion should follow the structure of GADT:
{-# LANGUAGE GADTs #-}
-- n represents structure of the function I apply to
-- o represents output type of the function
-- a represents argument type of the function (all arguments same type)
data FList n o a where
-- with Nil the function is the output
Nil :: FList o o a
-- with Cons the corresponding function takes one more argument
Cons :: a -> FList f o a -> FList (a -> f) o a
args0 = Nil :: FList Int Int Int -- will not apply an argument
args1 = Cons 1 args0 -- :: FList (Int -> Int) Int Int
args2 = Cons 2 args1 -- :: FList (Int -> Int -> Int) Int Int
args3 = Cons 3 args2 -- :: FList (Int -> Int -> Int -> Int) Int Int
-- n, not (n -> o)
listApply :: n -> FList n o a -> o
listApply fun Nil = fun
listApply fun (Cons p l) = listApply (fun p) l
main = print $ listApply (+) args2
three :: Int
three = listApply (+) (Cons 2 (Cons 1 Nil))
oof :: String
oof = listApply reverse (Cons "foo" Nil)
true :: Bool
true = listApply True Nil -- True
-- The return type can be different than the arguments:
showplus :: Int -> Int -> String
showplus x y = show (x + y)
zero :: String
zero = listApply showplus (Cons 2 (Cons 1 Nil))
Must say, that this looks quite elegant!
Even OP doesn't ask for other's people implementation. You can approach problem a bit differently, resulting in a different looking but neat API:
{-# LANGUAGE KindSignatures #-}
{-# LANGuAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
import Data.Proxy
data N = O | S N
p0 :: Proxy O
p1 :: Proxy (S O)
p2 :: Proxy (S (S O))
p0 = Proxy
p1 = Proxy
p2 = Proxy
type family ArityNFun (n :: N) (a :: *) (b :: *) where
ArityNFun O a b = b
ArityNFun (S n) a b = a -> ArityNFun n a b
listApply :: Proxy n -> ArityNFun n a b -> ArityNFun n a b
listApply _ = id
three :: Int
three = listApply p2 (+) 2 1
oof :: String
oof = listApply p1 reverse "foo"
true :: Bool
true = listApply p0 True
showplus :: Int -> Int -> String
showplus x y = show (x + y)
zero :: String
zero = listApply p2 showplus 0 0
Here we could use Nat from GHC.TypeLits, but then we'd need UndecidableInstances. The added sugar is not worth the trouble in this example.
If you want to make polymorphic version, that's also possible, but then index is not (n :: Nat) (a :: *) but (as :: [*]). Also making plusN could be a nice exercise, for both encodings.
Not the same thing, but I suspect you'll be interested in the free applicative functor, which the free library provides. It goes something like this (based on the implementation in free, but using a :<**> constructor instead of Ap):
data Ap f a where
Pure :: a -> Ap f a
(:<**>) :: f x -> Ap f (x -> a) -> Ap f a
You can think of these as a heterogeneously-typed list with elements of types f x0, ..., f xn, terminated by Pure (f :: x0 -> ... -> xn -> a). This is like a "syntax tree" for applicative computations, allowing you to use the regular applicative methods to build up a "tree" that can be separately run by interpreter functions.
Exercise: implement the following instances:
instance Functor f => Functor (Ap f) where ...
instance Functor f => Applicative (Ap f) where ...
Hint: the Applicative laws provide a recipe that you can use to implement these.

Function Definition in Haskell (GHCİ)

I am a beginner of Haskell. What is wrong with this expression?:
Prelude> let { f op [] = [] ; f op (h:t) = op h : f op t }
Prelude> f (+) []
<interactive>:337:1:
No instance for (Show (t0 -> t0))
arising from a use of `print'
Possible fix: add an instance declaration for (Show (t0 -> t0))
In a stmt of an interactive GHCi command: print it
Many thanks for support.
Function (+) has type (+) :: Num a => a -> a -> a and function f has type f :: (t -> a) -> [t] -> [a].
f expects a function of one argument.
f (+1) [] will be correct
Your function is inferred to have this type: (x -> y) -> [x] -> [y].
(+) has type Num a => a -> a -> a. This can be an instance of the type (x -> y) if we take x to be a and y to be a -> a, so all is fine. So the empty list in f (+) [] must be of type [a], and the return type must be [a -> a] (all of this with the same Num a constraint, of course). So f (+) [] correctly computes an empty list of type Num a => [a -> a].
This is all fine, but then GHCi wants to print the result of your expression. There is no way of printing values of type Num a => [a -> a], because there is no way to print functions. This is basically the error GHCi is giving you: No instance for (Show (t0 -> t0)).
There's nothing actually wrong with your function, or with your invocation of that function. It's just that it results in an (empty) list of functions, which you can't print. If you'd let bound it instead, you wouldn't get an error, and you could go on to do with it anything you would normally expect to be able to do with a Num a => [a -> a].
The type of the function f is:
Prelude> :t f
f :: (t -> a) -> [t] -> [a]
If you call the function like this:
Prelude> f (+) []
... you get the types (Let's pretend that (+) only works for Ints in this example):
(+) :: Int -> Int -> Int
(t -> a) = (Int -> (Int -> Int))
t = Int
a = (Int -> Int)
This means that the second argument to f is of type [t] = [Int] and the return type is [a] = [Int -> Int]. Because the return type is a list of functions, and functions cannot be shown, ghci will refuse to "compile" the expression because of the type errors, and you get the error that you see.
The f (+) [] function returns a list of functions:
Prelude> :t f (+) []
f (+) [] :: Num t => [t -> t]
And prelude trying to call show function on each element of the resulting list, however functions are not instances of the Show type class.

Resources