
Hello all, I was trying to construct a data type "Profile" which allows some set-like behavior. I started with functions to count, add and filter profiles. type Count = Int data Prof a = P { pCount :: Count, pFilter :: (a -> Bool) -> Prof a, pAdd :: (Prof a) -> (Prof a) } | NoProfile When I tried to actually implement a Profile, the first two functions were easy: exProfile :: [Int] -> Prof Int exProfile xs = P { pCount = length xs, pFilter = \p -> exProfile [x | x<-xs, p x], } But I didn't know how to implement pAdd. I was tempted to write something like pAdd = exProfile (xs ++ ys) But I wouldn't know where to take the ys from. I also hesitate to declare a toList function (which would solve the problem) in Prof. I would only do this *because* it is handy in exProfile, not because it is an intrinsic feature of Prof. What would be a good way to work with set-like behavior without being tied to one particular implementation?

On Sun, Mar 08, 2015 at 09:52:50PM +0100, martin wrote:
Hello all,
I was trying to construct a data type "Profile" which allows some set-like behavior. I started with functions to count, add and filter profiles.
type Count = Int
data Prof a = P { pCount :: Count, pFilter :: (a -> Bool) -> Prof a, pAdd :: (Prof a) -> (Prof a) } | NoProfile [...] What would be a good way to work with set-like behavior without being tied to one particular implementation?
How about data ProfOps prof = P { pCount :: prof -> Count pFilter :: (a -> Bool) -> prof -> prof pAdd :: prof -> prof } Then you can instantiate this package of operations at different types, for example listProf :: ProfOps [a] listProf = P { pCount = length, pFilter = filter, pAdd = (++) } If you really want the type parameter to be exposed you can use data ProfOps prof = P { pCount :: forall a. prof a -> Count pFilter :: forall a. (a -> Bool) -> prof a -> prof a pAdd :: forall a. prof a -> prof a } Tom

On Sun, Mar 08, 2015 at 09:01:59PM +0000, Tom Ellis wrote:
On Sun, Mar 08, 2015 at 09:52:50PM +0100, martin wrote:
Hello all,
I was trying to construct a data type "Profile" which allows some set-like behavior. I started with functions to count, add and filter profiles.
type Count = Int
data Prof a = P { pCount :: Count, pFilter :: (a -> Bool) -> Prof a, pAdd :: (Prof a) -> (Prof a) } | NoProfile [...] What would be a good way to work with set-like behavior without being tied to one particular implementation?
If you really want the type parameter to be exposed you can use
data ProfOps prof = P { pCount :: forall a. prof a -> Count pFilter :: forall a. (a -> Bool) -> prof a -> prof a pAdd :: forall a. prof a -> prof a }
Actually the above is the only version which works because pFilter mentions a. In that case you need the following package listProf :: ProfOps [] listProf = P { pCount = length, pFilter = filter, pAdd = (++) } I also made a mistake with the type of pAdd. It should be pAdd :: forall a. prof a -> prof a -> prof a

Am 03/08/2015 um 10:05 PM schrieb Tom Ellis:
If you really want the type parameter to be exposed you can use
data ProfOps prof = P { pCount :: forall a. prof a -> Count pFilter :: forall a. (a -> Bool) -> prof a -> prof a pAdd :: forall a. prof a -> prof a }
Actually the above is the only version which works because pFilter mentions a. In that case you need the following package
listProf :: ProfOps [] listProf = P { pCount = length, pFilter = filter, pAdd = (++) }
How would I use this? If I have a listProf and I want to perform pCount, must I then choose the pCount from listProf? Something like pCount listProf aProfile But then my code knows that I am using a list representation, which is the thing I wanted to avoid. I tried to pair the ops with the representation, but this also got me into trouble, because when I e.g. do a pAdd then it gets strange when the two operands do not use the same Ops.

On Mon, Mar 9, 2015 at 1:30 PM, martin
How would I use this? If I have a listProf and I want to perform pCount, must I then choose the pCount from listProf? Something like
pCount listProf aProfile
But then my code knows that I am using a list representation, which is the thing I wanted to avoid.
I tried to pair the ops with the representation, but this also got me into trouble, because when I e.g. do a pAdd then it gets strange when the two operands do not use the same Ops.
You could possibly hide the details by using GADTs, adding a new data constructor for each underlying type (which could also be abstracted as in the suggestion from Tom), but you still have to deal with an unavoidable issue -- you'll have to encode how to add collections of disparate types at some point. E.g., how do you add a `Prof Int` that's backed by a `Set a` to a `Prof Int` backed by a `[Int]` ? (at least I think that's unavoidable -- you may be able to solve it with a type class that moves the combination logic elsewhere.) --Rogan

On Mon, Mar 09, 2015 at 09:30:27PM +0100, martin wrote:
Am 03/08/2015 um 10:05 PM schrieb Tom Ellis: How would I use this? If I have a listProf and I want to perform pCount, must I then choose the pCount from listProf? Something like
pCount listProf aProfile
But then my code knows that I am using a list representation, which is the thing I wanted to avoid.
I tried to pair the ops with the representation, but this also got me into trouble, because when I e.g. do a pAdd then it gets strange when the two operands do not use the same Ops.
You use it like this. You can write operations on your Prof without having to know its concrete type. If you don't like threading the dictionary `p` around then you could use a reader monad or a typeclass. {-# LANGUAGE Rank2Types #-} type Count = Int data ProfOps prof = P { pCount :: forall a. prof a -> Count, pFilter :: forall a. (a -> Bool) -> prof a -> prof a, pAdd :: forall a. prof a -> prof a -> prof a } listProf :: ProfOps [] listProf = P { pCount = length, pFilter = filter, pAdd = (++) } example :: ProfOps prof -> prof Integer -> prof Integer -> Count example p profs1 profs2 = pCount p (pAdd p (pFilter p even profs1) profs2) Tom
participants (3)
-
martin
-
Rogan Creswick
-
Tom Ellis