How can I discard the Haskell generic recursive applicative functor

When using applicative functors in Haskell, I often come across situations in which I get duplicate code as follows:

instance Arbitrary MyType where arbitrary = MyType <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary 

In this example, I would like to say:

 instance Arbitrary MyType where arbitrary = applyMany MyType 4 arbitrary 

but I cannot figure out how to make applyMany (or something similar to it). I can't even figure out what type would be, but I would have to use a data constructor, Int (n) and a function to apply n times. This happens when creating instances for QuickCheck, SmallCheck, Data.Binary, Xml serialization, and other recursive situations.

So how can I define applyMany ?

+7
source share
6 answers

I think you can do this with the OverlappingInstances hack:

 {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, OverlappingInstances #-} import Test.QuickCheck import Control.Applicative class Arbitrable ab where convert :: Gen a -> Gen b instance (Arbitrary a, Arbitrable bc) => Arbitrable (a->b) c where convert a = convert (a <*> arbitrary) instance (a ~ b) => Arbitrable ab where convert = id -- Should work for any type with Arbitrary parameters data MyType abcd = MyType abcd deriving (Show, Eq) instance Arbitrary (MyType Char Int Double Bool) where arbitrary = convert (pure MyType) check = quickCheck ((\s -> s == s) :: (MyType Char Int Double Bool -> Bool)) 
+6
source

Check out the derive . Any other good generic library should be able to do this; get only the one I know. For example:

 {-# LANGUAGE TemplateHaskell #-} import Data.DeriveTH import Test.QuickCheck $( derive makeArbitrary ''MyType ) 

To solve the question that you really asked, FUZxxl is right, this is not possible in Haskell plain vanilla. As you point out, it is not clear what its type should be. This is possible with the metaprogramming of the Haskell pattern (not very pleasant). If you go this route, you probably should just use the generic library, which has already done some hard research for you. I believe that this is also possible using the nature of type and types, but, unfortunately, such decisions at the level of type types are usually difficult to ignore. Conor McBride is working on this issue .

+10
source

Here is what I got at least:

 {-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} module ApplyMany where import Control.Applicative import TypeLevel.NaturalNumber -- from type-level-natural-number package class GetVal a where getVal :: a class Applicative f => ApplyMany nfg where type Res ng app :: n -> fg -> f (Res ng) instance Applicative f => ApplyMany Zero fg where type Res Zero g = g app _ fg = fg instance (Applicative f, GetVal (fa), ApplyMany nfg) => ApplyMany (SuccessorTo n) f (a -> g) where type Res (SuccessorTo n) (a -> g) = Res ng app n fg = app (predecessorOf n) (fg<*>getVal) 

Usage example:

 import Test.QuickCheck data MyType = MyType Char Int Bool deriving Show instance Arbitrary a => GetVal (Gen a) where getVal = arbitrary test3 = app n3 (pure MyType) :: Gen MyType test2 = app n2 (pure MyType) :: Gen (Bool -> MyType) test1 = app n1 (pure MyType) :: Gen (Int -> Bool -> MyType) test0 = app n0 (pure MyType) :: Gen (Char -> Int -> Bool -> MyType) 

By the way, I think this solution is not very useful in the real world. Especially without local types.

+5
source

Not satisfied with my other answer, I came up with an amazing option.

 -- arb.hs import Test.QuickCheck import Control.Monad (liftM) data SimpleType = SimpleType Int Char Bool String deriving(Show, Eq) uncurry4 f (a,b,c,d) = fabcd instance Arbitrary SimpleType where arbitrary = uncurry4 SimpleType `liftM` arbitrary -- ^ this line is teh pwnzors. -- Note how easily it can be adapted to other "simple" data types 

 ghci> :l arb.hs [1 of 1] Compiling Main ( arb.hs, interpreted ) Ok, modules loaded: Main. ghci> sample (arbitrary :: Gen SimpleType) >>>a bunch of "Loading package" statements<<< SimpleType 1 'B' False "" SimpleType 0 '\n' True "" SimpleType 0 '\186' False "\208! \227" ... 

Long explanation of how I understood it

So, as I understand it. I was wondering: "How does an Arbitrary instance for (Int, Int, Int, Int) already exist? I'm sure no one wrote it, so I need to get it somehow. Of course, I found the following in docs for instances of an arbitrary file :

 (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d) 

Well, if they already have it, then why not abuse it? Simple types, which simply consist of smaller arbitrary data types, are not much different from a tuple.

So, now I need to somehow convert the “arbitrary” method to 4-tuples so that it works for my type. Most likely, launch is involved.

Stop. Hoogle time!

(We can easily define our own uncurry4 , so suppose we already have this to work with.)

I have a generator, arbitrary :: Gen (q,r,s,t) (where q, r, s, t are all instances of arbitrary). But let's just say it arbitrary :: Gen a . In other words, a represents (q,r,s,t) . I have a uncurry4 function that is of type (q -> r -> s -> t -> b) -> (q,r,s,t) -> b . Obviously, we will apply uncurry4 to our SimpleType constructor. So uncurry4 SimpleType has type (q,r,s,t) -> SimpleType . However, keep the return value because Hoogle does not know about our SimpleType. Therefore, remembering our definition of a , we have essentially uncurry4 SimpleType :: a -> b .

So, I have Gen a and the function a -> b . And I need a Gen b result. (Remember that for our situation, a is (q,r,s,t) , and b is SimpleType ). Therefore, I am looking for a function with such a signature of the type: Gen a -> (a -> b) -> Gen b . Hoogling, who , and knowing that Gen is an instance of Monad , I immediately recognize liftM as a monadic magic solution to my problem.

Hoogle is saving this day again. I knew that there was probably some kind of “lifting” combinator to get the desired result, but I honestly did not think to use liftM (durrr!) Until I pushed the type signature.

+5
source

Check out liftA2 and liftA3 . Alternatively, you can easily write your own applyTwice or applyThrice methods, for example:

 applyTwice :: (a -> a -> b) -> a -> b applyTwice fx = fxx applyThrice :: (a -> a -> a -> b) -> a -> b applyThrice fx = fxxx 

There is no easy way to get the general set of applications you are asking for, but writing trivial helpers like these is neither complicated nor unusual.


[edit] So, it turns out that you think something like this will work

 liftA4 fabcd = f <$> a <*> b <*> c <*> d quadraApply fx = fxxxx data MyType = MyType Int String Double Char instance Arbitrary MyType where arbitrary = (liftA4 MyType) `quadraApply` arbitrary 

But this is not so. (liftA4 MyType) has a signature of the type (Applicative f) => f Int -> f String -> f Double -> f Char -> f MyType . This is incompatible with the first quadraApply parameter, which has a signature of type (a -> a -> a -> a -> b) -> a -> b . It will only work for data structures that contain multiple values ​​of the same arbitrary type.

 data FourOf a = FourOf aaaa instance (Arbitrary a) => Arbitrary (FourOf a) where arbitrary = (liftA4 FourOf) `quadraApply` arbitrary ghci> sample (arbitrary :: Gen (FourOf Int)) 

Of course, you could just do it if you had this situation.

 ghci> :l +Control.Monad ghci> let uncurry4 f (a, b, c, d) = fabcd ghci> samples <- sample (arbitrary :: Gen (Int, Int, Int, Int)) ghci> forM_ samples (print . uncurry4 FourOf) 

There may be some kind of language pragma that can teach an “arbitrary” function to more diverse types of data. But it currently exceeds my Haskell-fu level.

+4
source

This is not possible with Haskell. The problem is that your function will have a type depending on a numeric argument. With a type system that allows dependent types, this should be possible, but I think not in Haskell.

What you can try is to use polymorphism and tyeclasses to archive this, but it can become hacked and you will need a large group of extensions to satisfy the compiler.

+2
source

All Articles