[Haskell-cafe] CoArbitrary

182 views
Skip to first unread message

Tony Morris

unread,
Feb 9, 2013, 2:07:54 AM2/9/13
to haskel...@haskell.org
Hello,
In the QuickCheck library, there is a type-class called CoArbitrary. It
is defined like so:

class CoArbitrary a where
coarbitrary :: a -> Gen b -> Gen b -- Gen is a monad

Its purpose is to allow generation of functions. In other words, for
taking Gen x -> Gen (a -> x), which could be done rather degenerately
(fmap const) but QuickCheck constrains with (CoArbitrary a) to perturb
the resulting value instead of ignoring (const) it.

It has always puzzled me in the general sense of thinking about the
scenario: f x -> f (a -> x) and whether the CoArbitrary is a
good/general solution, perhaps for the specific case of f=Gen or maybe
even more generally.

-- approximate
(a -> f x -> f x) -- e.g. CoArbitrary to perturb the result
-> f x -- e.g. Gen x
-> f (a -> x) -- e.g. Gen (a -> x)

So I often wonder about what might be a better (and perhaps more
general) constraint to produce functions f x -> f (a -> x) for a given
Monad f. I was wondering if there is an existing abstraction (or paper)
that might point me in that direction. It is a problem that I encounter
on occasion in general programming and I am using Arbitrary/CoArbitrary
as an example to help make my point, but I am dissatisfied (for reasons
that I am unsure about) with the solution provided by CoArbitrary.

What about other monads? For example, what is a general constraint to be
placed to permit a function Maybe x -> Maybe (a -> x) that does not
simply "const" away the argument.

I hope I have phrased this in a way to make the point. I found it a bit
difficult to articulate and I do wonder (hope!) that others encounter
similar scenarios. Thanks for any tips!

--
Tony Morris
http://tmorris.net/


_______________________________________________
Haskell-Cafe mailing list
Haskel...@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Stephen Tetley

unread,
Feb 9, 2013, 5:50:45 AM2/9/13
to Tony Morris, haskel...@haskell.org
I think GAST - the Clean equivalent of Quickcheck - generates
functions. There are certainly quite a few papers by members of the
Clean team documenting how they generate them.

On 9 February 2013 07:07, Tony Morris <tonym...@gmail.com> wrote:
[...]
> I hope I have phrased this in a way to make the point. I found it a bit
> difficult to articulate and I do wonder (hope!) that others encounter
> similar scenarios. Thanks for any tips!

Roman Cheplyaka

unread,
Feb 9, 2013, 4:08:41 PM2/9/13
to Stephen Tetley, haskel...@haskell.org
I don't think the question was about generating functions...
FWIW, both QuickCheck and SmallCheck generate functions. There was also
an interesting paper at the last ICFP by Koen related to this.

But I think Tony is looking for some kind of a pattern here...

Roman

* Stephen Tetley <stephen...@gmail.com> [2013-02-09 10:50:45+0000]

Tony Morris

unread,
Feb 10, 2013, 3:33:45 AM2/10/13
to Roman Cheplyaka, haskel...@haskell.org
On 09/02/13 15:08, Roman Cheplyaka wrote:
> I don't think the question was about generating functions...
> FWIW, both QuickCheck and SmallCheck generate functions. There was also
> an interesting paper at the last ICFP by Koen related to this.
>
> But I think Tony is looking for some kind of a pattern here...
>
> Roman
>
> * Stephen Tetley <stephen...@gmail.com> [2013-02-09 10:50:45+0000]
>> I think GAST - the Clean equivalent of Quickcheck - generates
>> functions. There are certainly quite a few papers by members of the
>> Clean team documenting how they generate them.
>>
>> On 9 February 2013 07:07, Tony Morris <tonym...@gmail.com> wrote:
>> [...]
>>> I hope I have phrased this in a way to make the point. I found it a bit
>>> difficult to articulate and I do wonder (hope!) that others encounter
>>> similar scenarios. Thanks for any tips!
>> _______________________________________________
>> Haskell-Cafe mailing list
>> Haskel...@haskell.org
>> http://www.haskell.org/mailman/listinfo/haskell-cafe
Yeah I am looking for a possible pattern. I am struggling to explain the
problem. Perhaps some code will help.

This code compiles, however the function "problem" is the one I am
looking for. There are two data structures here:
1) Op, which is a functor
2) FreeOp, which is a monad that arises from the Op functor (i.e. an
instance of the free monad)

There are some functions for demonstration:
1) productOp -- An example of zipping two FreeOp instances, just to show
that you can (though, this is trivial by the monad)
2) booleanOp -- Produces a FreeOp Bool by using the IntOp constructor for Op
3) coproductOp -- An example of splitting out two FreeOp instances, to
show this is possible too.

* The question is, what about a function FreeOp b -> FreeOp (a -> b)?
* Can I constrain the 'a' type variable somehow to come up with
something similar to CoArbitrary (QuickCheck)?
* Can I generalise this idea i.e. not just FreeOp? Or for CoArbitrary,
not just for Gen?
* Is there a pattern here that is currently not part of my mental tool
kit? I am struggling to see it; maybe just it's not there.

As always, thanks for any pointers!

Begin code...


data Op a =
DoubleOp (Double -> a)
| IntOp (Int -> a)

data FreeOp a =
Suspend (Op (FreeOp a))
| Point a

---- examples

productOp ::
FreeOp a
-> FreeOp b
-> FreeOp (a, b)
productOp a b =
do a' <- a
b' <- b
return (a', b')

boolOp ::
FreeOp Bool
boolOp =
Suspend (fmap Point (IntOp even))

coproductOp ::
FreeOp a
-> FreeOp b
-> FreeOp (Either a b)
coproductOp a b =
boolOp >>= \p -> if p then fmap Left a else fmap Right b

---- The Problem

problem ::
-- ? c =>
-- ? other arguments
FreeOp b
-> FreeOp (a -> b)
problem =
error "what constraints on 'a' to allow an implementation of this
function that uses the argument?"
-- fmap const -- type-checks, but ignores the argument, unlike e.g.
QuickCheck which uses CoArbitrary to "perturb" that result with the
argument.

---- support libraries

instance Functor Op where
fmap f (DoubleOp g) =
DoubleOp (f . g)
fmap f (IntOp g) =
IntOp (f . g)

instance Functor FreeOp where
fmap f =
(=<<) (return . f)

instance Monad FreeOp where
return =
Point
Suspend o >>= f =
Suspend (fmap (>>= f) o)
Point a >>= f =
f a


--
Tony Morris
http://tmorris.net/


Reply all
Reply to author
Forward
0 new messages