Minimum of Two Maybes - haskell

I want to get the minimum of two maybe values, or if one is nothing get the non-nothing one, or return nothing if both inputs are nothing. I can write a simple function to do this, but I suspect there is a way to do this without writing a custom function. Sorry, if this is a petty question, but is there a simpler way than using this custom function?
minMaybe :: Ord a => Maybe a -> Maybe a -> Maybe a
minMaybe Nothing b = b
minMaybe a Nothing = a
minMaybe (Just a) (Just b) = Just $ min a b

It is possible to satisfy the specification using operators from Control.Applicative.
myMin :: Ord x => Maybe x -> Maybe x -> Maybe x
myMin a b = min <$> a <*> b <|> a <|> b
where the <|> for Maybe implements "preference"
Nothing <|> b = b
a <|> _ = a
The thing is
min <$> Just a <*> Just b = Just (min a b)
but
min <$> Just a <*> Nothing = Nothing
which has resulted in some incorrect answers to this question. Using <|> allows you to prefer the computed min value when it's available, but recover with either individual when only one is Just.
But you should ask if it is appropriate to use Maybe in this way. With the inglorious exception of its Monoid instance, Maybe is set up to model failure-prone computations. What you have here is the extension of an existing Ord with a "top" element.
data Topped x = Val x | Top deriving (Show, Eq, Ord)
and you'll find that min for Topped x is just what you need. It's good to think of types as not just the representation of data but the equipment of data with structure. Nothing usually represents some kind of failure, so it might be better to use a different type for your purpose.

You cannot use the Applicative, or Monad instance for this, since any Nothing in those contexts will have your total result being a Nothing. That being said, the term "simpler" is highly opinionated, and your function is fine as it is.

You can write it using the Alternative instance of Maybe:
minMaybe a b = liftA2 min a b <|> a <|> b
Alternatively, you could use maxBound as default, so it'll always choose the other:
minMaybe a b = liftA2 min (d a) (d b)
where d x = x <|> Just maxBound
But I don't recommend that.

Question is about lifting function min :: Ord a ⇒ a → a → a to work with Maybes context. It's associative so Semigroup instance does exactly what you want:
min' :: forall a. Ord a => Maybe a -> Maybe a -> Maybe a
min' = coerce ((<>) #(Maybe (Min a)))
This requires ScopedTypeVariables and TypeApplications. coerce comes from Data.Coerce. More old fashioned solution given below. But version above should be more performant: coerce doesn't exist at runtime. Although GHC may eliminate fmaps there's no guarantee:
min'' :: Ord a => Maybe a -> Maybe a -> Maybe a
min'' x y = fmap getMin (fmap Min x <> fmap Min y)
P.S. I would say that your solution is just fine.

I think radomaj had a good idea.
import Data.Ord (Down (..))
import Data.Function (on)
minMaybes mx my =
getDown <$> (max `on` fmap Down) mx my
getDown (Down x) = x
We use max to prefer Just to Nothing, and then use Down so we actually get the minimum if both are Just.
Here's another, similar approach that seems a bit cleaner. Maybe can be seen a way to tack on an extra minimal value, Nothing, to an arbitrary Ord type. We can write our own type to tack on a maximal value:
data AddMax a = TheMax | Plain a deriving Eq
instance Ord a => Ord (AddMax a) where
TheMax <= Plain _ = False
_ <= TheMax = True
Plain a <= Plain b = a <= b
maybeToAddMax :: Maybe a -> AddMax a
maybeToAddMax = maybe TheMax Plain
addMaxToMaybe :: AddMax a -> Maybe a
addMaxToMaybe TheMax = Nothing
addMaxToMaybe (Plain a) = Just a
Now you can write
minMaybes mx my = addMaxToMaybe $
(min `on` maybeToAddMax) mx my
You can also pull a little sleight-of-hand:
{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
import Data.Ord
import Data.Function
import Data.Coerce
newtype AddMax a = AddMax {unAddMax :: Down (Maybe (Down a))}
deriving (Eq, Ord)
Now
minMaybes :: forall a. Ord a => Maybe a -> Maybe a -> Maybe a
minMaybes = coerce (min #(AddMax a))

Related

What are the best ways to do field-wise datatype operations?

Say I have a datatype like:
data CpuStatRow a = CpuStatRow
{ user :: a
, nice :: a
, system :: a
, idle :: a
, ioWait :: a
, irq :: a
, softIrq :: a
, steal :: a
}
which comes from parsing CPU info from /proc/stat of a linux OS, in which every numeric field is an accumulative value since boot time. So if I want to figure out values during certain period, I need to grab before :: CpuStatRow Int and after :: CpuStatRow Int and do the field-wise difference (assuming proper language extensions):
-- let's not worry about distinction between a raw value and a difference for now ...
type Diff = Int
getDiff :: CpuStatRow Int -> CpuStatRow Int -> CpuStatRow Diff
getDiff after before = CpuStatRow {..}
where
diffOn prj = prj after - prj before
user = diffOn user
nice = diffOn nice
... -- basically same for each field
I'm wondering is there something better to do that:
generalize to function of other aritieis (e.g. lift a function f :: a -> b -> c -> d to some f' :: F a -> F b -> F c -> F d
can easily work on any datatype, as there are many accumulative values in /proc, applying the same method above is not fun. I have Applicative in mind but that doesn't seems to be "deriving-able" by ghc
If you want to generalize to functions of any arity and any argument type, I think Applicative is the best you will find. Indeed you won't be able to derive it, but at least you can implement pure and (<*>) once and then reuse it for all of your functions rather than having to do the tedium for each of them. And you can ask GHC to derive the Functor instance for you, if nothing else.
If you want to work with "accumulative values" instead of general functions, you could consider adding a Monoid (or just Semigroup) instance, of the form
instance Semigroup a => Semigroup (CpuStatRow a) where
(CpuStatRow x y z ...) <> (CpuStatRow x' y' z') = CpuStatRow (x <> x') (y <> y') (z <> z') ...
As you can imagine that would get tiring quickly, but fortunately you can implement it in terms of Applicative - we're already saving time!
instance Semigroup a => Semigroup (CpuStatRow a) where
(<>) = liftA2 (<>)
instance Monoid a => Monoid (CpuStatRow a) where
mempty = pure mempty

Combining validators in applicative style in Haskell

I have a good grasp on imperative programming, but now I learn myself a Haskell for great good.
I think, I have a good theoretical understanding of Monads, Functors and Applicatives, but I need some practice. And for practice I sometimes bring some bits from my current work tasks.
And I'm stuck a bit with combining stuff in applicative way
First question
I have two functions for validation:
import Prelude hiding (even)
even :: Integer -> Maybe Integer
even x = if rem x 2 == 0 then Just x else Nothing
isSmall :: Integer -> Maybe Integer
isSmall x = if x < 10 then Just x else Nothing
Now I want validate :: Integer -> Maybe Integer built from even and isSmall
My best solution is
validate a = isSmall a *> even a *> Just a
And it's not point free
I can use a monad
validate x = do
even x
isSmall x
return x
But why use Monad, if (I suppose) all I need is an Applicative? (And it still not point free)
Is it a better (and more buitiful way) to do that?
Second question
Now I have two validators with different signatures:
even = ...
greater :: (Integer, Integer) -> Maybe (Integer, Integer)
-- tuple's second element should be greater than the first
greater (a, b) = if a >= b then Nothing else Just (a, b)
I need validate :: (Integer, Integer) -> Maybe (Integer, Integer), which tries greater on the input tuple and then even on the tuple's second element.
And validate' :: (Integer, Integer) -> Maybe Integer with same logic, but returning tuple's second element.
validate (a, b) = greater (a, b) *> even b *> Just (a, b)
validate' (a, b) = greater (a, b) *> even b *> Just b
But I imagine that the input tuple "flows" into greater, then "flows" into some kind of composition of snd and even and then only single element ends up in the final Just.
What would a haskeller do?
When you are writing validators of the form a -> Maybe b you are more interested in that whole type than in the Maybe applicative. The type a -> Maybe b are the Kleisli arrows of the Maybe monad. You can make some tools to help work with this type.
For the first question you can define
(>*>) :: Applicative f => (t -> f a) -> (t -> f b) -> t -> f b
(f >*> g) x = f x *> g x
infixr 3 >*>
and write
validate = isSmall >*> even
Your second examples are
validate = even . snd >*> greater
validate' = even . snd >*> fmap snd . greater
These check the conditions in a different order. If you care about evaluation order you can define another function <*<.
ReaderT
If you use the type a -> Maybe b a lot it might be worth creating a newtype for it so that you can add your own instances for what you want it to do. The newtype already exists; it's ReaderT, and its instances already do what you want to do.
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
When you use the type r -> Maybe a as a validator to validate and transform a single input r it's the same as ReaderT r Maybe. The Applicative instance for ReaderT combines two of them together by applying both of their functions to the same input and then combining them together with <*>:
instance (Applicative m) => Applicative (ReaderT r m) where
f <*> v = ReaderT $ \ r -> runReaderT f r <*> runReaderT v r
...
ReaderT's <*> is almost exactly the same as >*> from the first section, but it doesn't discard the first result. ReaderT's *> is exactly the same as >*> from the first section.
In terms of ReaderT your examples become
import Control.Monad.Trans.ReaderT
checkEven :: ReaderT Integer Maybe Integer
checkEven = ReaderT $ \x -> if rem x 2 == 0 then Just x else Nothing
checkSmall = ReaderT Integer Maybe Integer
checkSmall = ReaderT $ \x -> if x < 10 then Just x else Nothing
validate = checkSmall *> checkEven
and
checkGreater = ReaderT (Integer, Integer) Maybe (Integer, Integer)
checkGreater = ReaderT $ \(a, b) = if a >= b then Nothing else Just (a, b)
validate = checkGreater <* withReaderT snd checkEven
validate' = snd <$> validate
You use one of these ReaderT validators on a value x by runReaderT validate x
You ask why use Monad if all you need is Applicative? I can ask -- why use Applicative if all you need is Monoid?
All you're doing is essentially trying to take advantage of monoidal behavior/a Monoid, but trying to do it through an Applicative interface. Kind of like working with Int's through their string representation (implementing + for the strings "1" and "12" and working with strings instead of just 1 and 12 and working with ints)
Note that you can get an Applicative instance from any Monoid instance, so finding a Monoid that can solve your problem is the same as finding an Applicative that can.
even :: Integer -> All
even x = All (rem x 2 == 0)
isSmall :: Integer -> All
isSmall x = All (x < 10)
greater :: (Integer, Integer) -> All
greater (a, b) = All (b > a)
To prove that they are the same, we can write conversion functions back and forth:
convertToMaybeFunc :: (a -> All) -> (a -> Maybe a)
convertToMaybeFunc f x = guard (getAll (f x)) $> x
-- assuming the resulting Just contains no new information
convertFromMaybeFunc :: (a -> Maybe b) -> (a -> All)
convertFromMaybeFunc f x = maybe (All False) (\_ -> All True) (f x)
You could directly write your validate:
validate :: Int -> All
validate a = isSmall a <> even a
But you could also write it in the lifted style you want:
validate :: Int -> All
validate = isSmall <> even
Want do notation?
validate :: Int -> All
validate = execWriter $ do
tell isSmall
tell even
tell (other validator)
validate' :: (Int, Int) -> All
validate' = execWriter $ do
tell (isSmall . fst)
tell (isSmall . snd)
tell greater
As you can see, every Monoid instance begets an Applicative/Monad instance (through Writer and tell), which makes this a bit convenient. You can think of Writer/tell here as "lifting" a Monoid instance to a free Applicative/Monad instance.
In the end, you're noticing a design pattern/abstraction that is useful, but it's really the monoid that you're noticing. You're fixated on working on that monoid through an Applicative interface, somehow...but it might be simpler to just work with the monoid directly.
Also,
validate :: Int -> All
validate = mconcat
[ isSmall
, even
, other validator
]
is arguably comparable in clarity to the do notation version w/ Writer :)

Haskell Arbitrary Instance of Higher Order type

I have the type and it is an instance of SemiGroup.
I want to write a quickCheck method to ensure that it is correct.
How do I create an Arbitrary instance of this type?
newtype Combine a b =
Combine { unCombine :: a -> b }
instance Semigroup b => Semigroup (Combine a b) where
x <> y = Combine $ \n -> unCombine x n <> unCombine y n
instance (Q.Arbitrary a, Num a) => Q.Arbitrary (Combine a b) where
arbitrary = do
a <- Q.arbitrary
return $ Combine (\n -> Sum(n+1)) a
The minimal fix is to turn on FlexibleInstances and write:
instance (Q.Arbitrary a, Num a) => Q.Arbitrary (Combine a (Sum a)) where
arbitrary = do
a <- Q.arbitrary :: Q.Gen ()
return $ Combine (\n -> Sum(n+1))
However, this is a bit unsatisfactory: I observe that a is completely unused (hence has to be manually given a type signature!), and the distribution over functions is kind of boring, since it returns the function (+1) (up to newtype wrapping) with probability 1. (Perhaps you wanted return $ Combine (\n -> Sum (n+a))? But even then the class of functions you can produce this way is a little bit boring. I'm not sure what you really meant, and I won't spend long speculating.)
If you want to do it right, you should produce a random output for each input. This is pretty convenient with the universe package:
import Data.Universe
import Data.Universe.Instances.Reverse
instance (Ord a, Finite a, Q.Arbitrary b) => Q.Arbitrary (Combine a b) where
arbitrary = Combine <$> sequenceA (const Q.arbitrary)
As a side benefit, this does not require any extensions. However, you should be careful to choose input types with small domains -- Combine Bool (Sum Int) should be fine, or even Combine Word8 (Sum Word8), but if you try to test a property on a type like Combine Int (Sum Int) you will be sorry!

Monoid mempty in pattern matching

I tried to write a generalized maximum function similar to the one in Prelude. My first naiv approach looked like this:
maximum' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum' mempty = Nothing
maximum' xs = Just $ F.foldl1 max xs
However, when I test it it always returns Nothing regardless of the input:
> maximum' [1,2,3]
> Nothing
Now I wonder whether it's possible to obtain the empty value of a Monoid type instance. A test function I wrote works correctly:
getMempty :: (Monoid a) => a -> a
getMempty _ = mempty
> getMempty [1,2,3]
> []
I had already a look at these two questions but I didn't figure out how the answers solve my problem:
Write a Maximum Monoid using Maybe in Haskell
Haskell Pattern Matching on the Empty Set
How would I rewrite the maximum' function to get it to work ?
As C. A. McCann points out in his comment, you can't pattern match on values, only patterns.
The equation maximum' mempty = Nothing is actually equivalent to the equation maximum' x = Nothing. The argument gets bound to a name and Nothing is returned.
Here's a way to make your code work:
maximum' :: (F.Foldable a, Ord b, Eq (a b), Monoid (a b)) => a b -> Maybe b
maximum' xs
| xs == mempty = Nothing
| otherwise = Just $ F.foldl1 max xs
I.e. you can compare the value xs against mempty. Note that we need a Monoid constraint to be able to get at the value mempty :: a b and an Eq constraint to be able to compare as well.
An other, more elegant, solution would be to use a fold to differentiate between the empty and non-empty cases:
maximum'' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum'' xs = F.foldl max' Nothing xs
where max' Nothing x = Just x
max' (Just y) x = Just $ max x y
There are a few ways to do this (the one #opqdonut demonstrates is good). One could also make a "maximum" monoid around Maybe, and use foldMap.
newtype Maximum a = Max { unMaximum :: Maybe a }
instance (Ord a) => Monoid (Maximum a) where
mempty = Max Nothing
mappend (Max Nothing) b = b
mappend a (Max Nothing) = a
mappend (Max (Just a)) (Max (Just b)) = Max . Just $ (max a b)
maximum' = unMaximum . F.foldMap (Max . Just)
There are many ways, one is (as you mention) to create an instance of Monoid. However, we need to wrap it to Maybe to distinguish the case when we have no values. The implementation might look like this:
import Data.Monoid (Monoid, mempty, mappend)
import qualified Data.Foldable as F
-- Either we have a maximum value, or Nothing, if the
-- set of values is empty.
newtype Maximum a = Maximum { getMaximum :: Maybe a }
deriving (Eq, Ord, Read, Show)
instance Ord a => Monoid (Maximum a) where
mempty = Maximum Nothing
-- If one part is Nothing, just take the other one.
-- If both have a value, take their maximum.
(Maximum Nothing) `mappend` y = y
x `mappend` (Maximum Nothing) = x
(Maximum (Just x)) `mappend` (Maximum (Just y))
= Maximum (Just $ x `max` y)
maximum' :: (F.Foldable t, Ord a) => t a -> Maximum a
maximum' = F.foldMap (Maximum . Just)
As many have already told you, you can't pattern match on a value.
As fewer people have told you, pattern matching is arguably the Haskell equivalent of object fields in a language like Java: it's valuable for internal consumption by tightly coupled code, but probably not something you wish to expose to external client code. Basically, if you let a piece of code know your type's constructors, now you can never change these constructors without changing that other piece of code—even if your type's semantics did not really change.
The best solution here is really to just use Foldable.foldr:
maximum' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum' = F.foldr step Nothing
where step x Nothing = Just x
step x (Just y) = Just (max x y)
Note that foldr is a generalized destructor or eliminator for Foldable instances: its two arguments are "what to do with a non-empty Foldable" and "what to do with mempty. This is more abstract and reusable than pattern matching.
How about
maximum' :: (Monoid (t a), F.Foldable t, Ord a, Eq (t a)) => t a -> Maybe a
maximum' xs
| xs == mempty = Nothing
| otherwise = Just $ F.foldl1 max xs
You were missing a guard.
On the getEmpty function, you don't need it. Just use mempty, and allow its type to be inferred.

How can I abstract a common Haskell recursive applicative functor pattern

While using applicative functors in Haskell I've often run into situations where I end up with repetitive code like this:
instance Arbitrary MyType where
arbitrary = MyType <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
In this example I'd like to say:
instance Arbitrary MyType where
arbitrary = applyMany MyType 4 arbitrary
but I can't figure out how to make applyMany (or something similar to it). I can't even figure out what the type would be but it would take a data constructor, an 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 could I define applyMany?
Check out derive. Any other good generics library should be able to do this as well; derive is just the one I am familiar with. For example:
{-# LANGUAGE TemplateHaskell #-}
import Data.DeriveTH
import Test.QuickCheck
$( derive makeArbitrary ''MyType )
To address the question you actually asked, FUZxxl is right, this is not possible in plain vanilla Haskell. As you point out, it is not clear what its type should even be. It is possible with Template Haskell metaprogramming (not too pleasant). If you go that route, you should probably just use a generics library which has already done the hard research for you. I believe it is also possible using type-level naturals and typeclasses, but unfortunately such type-level solutions are usually difficult to abstract over. Conor McBride is working on that problem.
I think you can do it with OverlappingInstances hack:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, OverlappingInstances #-}
import Test.QuickCheck
import Control.Applicative
class Arbitrable a b where
convert :: Gen a -> Gen b
instance (Arbitrary a, Arbitrable b c) => Arbitrable (a->b) c where
convert a = convert (a <*> arbitrary)
instance (a ~ b) => Arbitrable a b where
convert = id
-- Should work for any type with Arbitrary parameters
data MyType a b c d = MyType a b c d 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))
Not satisfied with my other answer, I have come up with an awesomer one.
-- 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) = f a b c d
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"
...
Lengthy explanation of how I figured this out
So here's how I got it. I was wondering, "well how is there already an Arbitrary instance for (Int, Int, Int, Int)? I'm sure no one wrote it, so it must be derived somehow. Sure enough, I found the following in the docs for instances of Arbitrary:
(Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
Well, if they already have that defined, then why not abuse it? Simple types that are merely composed of smaller Arbitrary data types are not much different than just a tuple.
So now I need to somehow transform the "arbitrary" method for the 4-tuple so that it works for my type. Uncurrying is probably involved.
Stop. Hoogle time!
(We can easily define our own uncurry4, so assume we already have this to operate 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's arbitrary :: Gen a. In other words, a represents (q,r,s,t). I have a function, uncurry4, which has type (q -> r -> s -> t -> b) -> (q,r,s,t) -> b. We are obviously going to apply uncurry4 to our SimpleType constructor. So uncurry4 SimpleType has type (q,r,s,t) -> SimpleType. Let's keep the return value generic, though, because Hoogle doesn't know about our SimpleType. So remembering our definition of a, we have essentially uncurry4 SimpleType :: a -> b.
So I've got a Gen a and a function a -> b. And I want a Gen b result. (Remember, for our situation, a is (q,r,s,t) and b is SimpleType). So I am looking for a function with this type signature: Gen a -> (a -> b) -> Gen b. Hoogling that, and knowing that Gen is an instance of Monad, I immediately recognize liftM as the monadical-magical solution to my problems.
Hoogle saves the day again. I knew there was probably some "lifting" combinator to get the desired result, but I honestly didn't think to use liftM (durrr!) until I hoogled the type signature.
Here is what I'v 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 n f g where
type Res n g
app :: n -> f g -> f (Res n g)
instance Applicative f => ApplyMany Zero f g where
type Res Zero g = g
app _ fg = fg
instance
(Applicative f, GetVal (f a), ApplyMany n f g)
=> ApplyMany (SuccessorTo n) f (a -> g)
where
type Res (SuccessorTo n) (a -> g) = Res n g
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)
Btw, I think this solution is not very useful in real world. Especially without local type-classes.
Check out liftA2 and liftA3. Also, you can easily write your own applyTwice or applyThrice methods like so:
applyTwice :: (a -> a -> b) -> a -> b
applyTwice f x = f x x
applyThrice :: (a -> a -> a -> b) -> a -> b
applyThrice f x = f x x x
There's no easy way I can see to get the generic applyMany you're asking for, but writing trivial helpers such as these is neither difficult nor uncommon.
[edit] So it turns out, you'd think something like this would work
liftA4 f a b c d = f <$> a <*> b <*> c <*> d
quadraApply f x = f x x x x
data MyType = MyType Int String Double Char
instance Arbitrary MyType where
arbitrary = (liftA4 MyType) `quadraApply` arbitrary
But it doesn't. (liftA4 MyType) has a type signature of (Applicative f) => f Int -> f String -> f Double -> f Char -> f MyType. This is incompatible with the first parameter of quadraApply, which has a type signature of (a -> a -> a -> a -> b) -> a -> b. It would only work for data structures that hold multiple values of the same Arbitrary type.
data FourOf a = FourOf a a a a
instance (Arbitrary a) => Arbitrary (FourOf a) where
arbitrary = (liftA4 FourOf) `quadraApply` arbitrary
ghci> sample (arbitrary :: Gen (FourOf Int))
Of course you could just do this if you had that situation
ghci> :l +Control.Monad
ghci> let uncurry4 f (a, b, c, d) = f a b c d
ghci> samples <- sample (arbitrary :: Gen (Int, Int, Int, Int))
ghci> forM_ samples (print . uncurry4 FourOf)
There might be some language pragma that can shoehorn the "arbitrary" function into the more diverse data types. But that's currently beyond my level of Haskell-fu.
This is not possible with Haskell. The problem is, that your function will have a type, that depends on the numeric argument. With a type system that allows dependent types, that should be possible, but I guess not in Haskell.
What you can try is using polymorphism and tyeclasses to archieve this, but it could become hacky and you need a big bunch of extensions to satisfy the compiler.

Resources