Design Question - Functions taking 'subtype' like arguments but has to be restricted to one type

Hello All, I have a design question where I could use some of your thoughts and suggestions. Here is the setup. I apologize for the long email but I have resisted asking this question, since it needs a long explanation, until now. I have a set of data structures defined as follows -- An Item that can be of two types, one whose value can be changed, one whose value are frozen once created data Item = FreeToChange {freeToChangeCount:: Int} | CannotChange {frozenCount:: Int} -- The item is part of a basket data Basket = Basket { name:: String, item::Item } -- The cart can have both kind of Baskets at the start of the program, or during runtime. data Cart = List Basket You can imagine this be a shopping cart with fixed set of items. Where the count of some of the items in the basket can be changed during shopping but not the count of the items once they are tagged as frozen. Therefore, valid operation are: 1. I can create an Basket with either FreeToChange item or CannotChange item. 2. I can update the count for FreeToChange item in the Basket 3. But, once I create an instance of the Basket to contain the CannotChange item, we cannot update the count anymore or update the Basket. One approach I have taken is to throw an error if this happens by pattern matching on type. But, this is an runtime check. addToBasket :: Basket -> Basket addToBasket b = let i = item b i' = case i of FreeToChange f -> FreeToChange {freeToChangeCount = f + 1} CannotChange f -> error ("This operation is not allowed") in b {item=i'} Here are my questions: 1. Is there a way to design the above data structures in such a way I could use the type system. 2. Since, these are runtime changes, is it even a good design pattern to push this responsibility to a type system? Thanks Guru

(Apologies for any duplicates... for some reason the original's reply
address is haskell-cafe@googlegroups.com rather than
haskell-cafe@haskell.org, and it sent a rejection notice for my reply.)
If I understand your model right, the Item is just a count, and it's the
Basket's 'name' field which actually says what the item is? Assuming I have
that right, check this out:
-- A basket is the association of an item with a count.
data Basket = Basket {item :: String, count :: Int}
-- Open and closed baskets are just baskets with different behavior.
newtype OpenBasket = OpenBasket {getOpenBasket :: Basket}
newtype ClosedBasket = ClosedBasket {getClosedBasket :: Basket}
-- A cart can contain a mix of both basket types.
type CartBasket = Either OpenBasket ClosedBasket
data Cart = List CartBasket
-- We'll want to conveniently inspect CartBaskets.
getCartBasket :: CartBasket -> Basket
getCartBasket = either getOpenBasket getClosedBasket
-- This function accepts only OpenBaskets. No need for a runtime check.
addToOpenBasket :: OpenBasket -> OpenBasket
addToOpenBasket (OpenBasket b@(Basket _ i))
= OpenBasket (b {count = i+1})
-- First-class functions let you keep this safety all the way to the top...
data UserControl a = UserControl {label :: String, behavior :: a -> a}
-- Only allow the user to see valid behaviors!
getAddToCartBasketUserControl :: CartBasket -> UserControl CartBasket
getAddToCartBasketUserControl =
let f = either (Left . addToOpenBasket) Right
in either (const (UserControl "Add" f)) (const (UserControl "Locked" f))
On Sat, Jul 30, 2016 at 3:01 PM, Gurudev Devanla
Hello All,
I have a design question where I could use some of your thoughts and suggestions. Here is the setup. I apologize for the long email but I have resisted asking this question, since it needs a long explanation, until now.
I have a set of data structures defined as follows
-- An Item that can be of two types, one whose value can be changed, one whose value are frozen once created data Item = FreeToChange {freeToChangeCount:: Int} | CannotChange {frozenCount:: Int}
-- The item is part of a basket data Basket = Basket { name:: String, item::Item }
-- The cart can have both kind of Baskets at the start of the program, or during runtime. data Cart = List Basket
You can imagine this be a shopping cart with fixed set of items. Where the count of some of the items in the basket can be changed during shopping but not the count of the items once they are tagged as frozen.
Therefore, valid operation are:
1. I can create an Basket with either FreeToChange item or CannotChange item. 2. I can update the count for FreeToChange item in the Basket 3. But, once I create an instance of the Basket to contain the CannotChange item, we cannot update the count anymore or update the Basket.
One approach I have taken is to throw an error if this happens by pattern matching on type. But, this is an runtime check.
addToBasket :: Basket -> Basket addToBasket b = let i = item b i' = case i of FreeToChange f -> FreeToChange {freeToChangeCount = f + 1} CannotChange f -> error ("This operation is not allowed") in b {item=i'}
Here are my questions:
1. Is there a way to design the above data structures in such a way I could use the type system. 2. Since, these are runtime changes, is it even a good design pattern to push this responsibility to a type system?
Thanks Guru
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Ha, whoops... I duplicated an error in your original because it compiles.
-- This creates a constructor named "List" which holds a single CartBasket!
data Cart = List CartBasket
-- You probably intended this:
type Cart = [CartBasket]
On Sat, Jul 30, 2016 at 5:55 PM, Theodore Lief Gannon
(Apologies for any duplicates... for some reason the original's reply address is haskell-cafe@googlegroups.com rather than haskell-cafe@haskell.org, and it sent a rejection notice for my reply.)
If I understand your model right, the Item is just a count, and it's the Basket's 'name' field which actually says what the item is? Assuming I have that right, check this out:
-- A basket is the association of an item with a count. data Basket = Basket {item :: String, count :: Int}
-- Open and closed baskets are just baskets with different behavior. newtype OpenBasket = OpenBasket {getOpenBasket :: Basket} newtype ClosedBasket = ClosedBasket {getClosedBasket :: Basket}
-- A cart can contain a mix of both basket types. type CartBasket = Either OpenBasket ClosedBasket data Cart = List CartBasket
-- We'll want to conveniently inspect CartBaskets. getCartBasket :: CartBasket -> Basket getCartBasket = either getOpenBasket getClosedBasket
-- This function accepts only OpenBaskets. No need for a runtime check. addToOpenBasket :: OpenBasket -> OpenBasket addToOpenBasket (OpenBasket b@(Basket _ i)) = OpenBasket (b {count = i+1})
-- First-class functions let you keep this safety all the way to the top... data UserControl a = UserControl {label :: String, behavior :: a -> a}
-- Only allow the user to see valid behaviors! getAddToCartBasketUserControl :: CartBasket -> UserControl CartBasket getAddToCartBasketUserControl = let f = either (Left . addToOpenBasket) Right in either (const (UserControl "Add" f)) (const (UserControl "Locked" f))
On Sat, Jul 30, 2016 at 3:01 PM, Gurudev Devanla < gurudev.devanla@gmail.com> wrote:
Hello All,
I have a design question where I could use some of your thoughts and suggestions. Here is the setup. I apologize for the long email but I have resisted asking this question, since it needs a long explanation, until now.
I have a set of data structures defined as follows
-- An Item that can be of two types, one whose value can be changed, one whose value are frozen once created data Item = FreeToChange {freeToChangeCount:: Int} | CannotChange {frozenCount:: Int}
-- The item is part of a basket data Basket = Basket { name:: String, item::Item }
-- The cart can have both kind of Baskets at the start of the program, or during runtime. data Cart = List Basket
You can imagine this be a shopping cart with fixed set of items. Where the count of some of the items in the basket can be changed during shopping but not the count of the items once they are tagged as frozen.
Therefore, valid operation are:
1. I can create an Basket with either FreeToChange item or CannotChange item. 2. I can update the count for FreeToChange item in the Basket 3. But, once I create an instance of the Basket to contain the CannotChange item, we cannot update the count anymore or update the Basket.
One approach I have taken is to throw an error if this happens by pattern matching on type. But, this is an runtime check.
addToBasket :: Basket -> Basket addToBasket b = let i = item b i' = case i of FreeToChange f -> FreeToChange {freeToChangeCount = f + 1} CannotChange f -> error ("This operation is not allowed") in b {item=i'}
Here are my questions:
1. Is there a way to design the above data structures in such a way I could use the type system. 2. Since, these are runtime changes, is it even a good design pattern to push this responsibility to a type system?
Thanks Guru
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

@Theodore, thank you for the working example.
You did simplify the problem statement. Actually Basket itself is a more
complicated structure, with Item being one of its attributes. But, let me
go along with your simplification.
It seems to me that, your approach replaces placeholder "error" expression
I had, with a no-op on the Right value. Of course, I could switch the
right function to 'either' to do some thing more than a no-op. Is that
correct?
@Imants, @Matthias : I agree with your suggestions and that is where
@Theodore's approach was also leading to but in a different form.
Looks like unless I have 2 separate entities, one for Frozen and one for
Flexible, I will have to deal with run-time behaviour. I say this, since in
a more complicated scenario, I might want to map over a list of buckets,.
but only be able to called updated on items in the list that are of
particular type.
Thanks
Guru
On Sat, Jul 30, 2016 at 5:55 PM, Theodore Lief Gannon
(Apologies for any duplicates... for some reason the original's reply address is haskell-cafe@googlegroups.com rather than haskell-cafe@haskell.org, and it sent a rejection notice for my reply.)
If I understand your model right, the Item is just a count, and it's the Basket's 'name' field which actually says what the item is? Assuming I have that right, check this out:
-- A basket is the association of an item with a count. data Basket = Basket {item :: String, count :: Int}
-- Open and closed baskets are just baskets with different behavior. newtype OpenBasket = OpenBasket {getOpenBasket :: Basket} newtype ClosedBasket = ClosedBasket {getClosedBasket :: Basket}
-- A cart can contain a mix of both basket types. type CartBasket = Either OpenBasket ClosedBasket data Cart = List CartBasket
-- We'll want to conveniently inspect CartBaskets. getCartBasket :: CartBasket -> Basket getCartBasket = either getOpenBasket getClosedBasket
-- This function accepts only OpenBaskets. No need for a runtime check. addToOpenBasket :: OpenBasket -> OpenBasket addToOpenBasket (OpenBasket b@(Basket _ i)) = OpenBasket (b {count = i+1})
-- First-class functions let you keep this safety all the way to the top... data UserControl a = UserControl {label :: String, behavior :: a -> a}
-- Only allow the user to see valid behaviors! getAddToCartBasketUserControl :: CartBasket -> UserControl CartBasket getAddToCartBasketUserControl = let f = either (Left . addToOpenBasket) Right in either (const (UserControl "Add" f)) (const (UserControl "Locked" f))
On Sat, Jul 30, 2016 at 3:01 PM, Gurudev Devanla < gurudev.devanla@gmail.com> wrote:
Hello All,
I have a design question where I could use some of your thoughts and suggestions. Here is the setup. I apologize for the long email but I have resisted asking this question, since it needs a long explanation, until now.
I have a set of data structures defined as follows
-- An Item that can be of two types, one whose value can be changed, one whose value are frozen once created data Item = FreeToChange {freeToChangeCount:: Int} | CannotChange {frozenCount:: Int}
-- The item is part of a basket data Basket = Basket { name:: String, item::Item }
-- The cart can have both kind of Baskets at the start of the program, or during runtime. data Cart = List Basket
You can imagine this be a shopping cart with fixed set of items. Where the count of some of the items in the basket can be changed during shopping but not the count of the items once they are tagged as frozen.
Therefore, valid operation are:
1. I can create an Basket with either FreeToChange item or CannotChange item. 2. I can update the count for FreeToChange item in the Basket 3. But, once I create an instance of the Basket to contain the CannotChange item, we cannot update the count anymore or update the Basket.
One approach I have taken is to throw an error if this happens by pattern matching on type. But, this is an runtime check.
addToBasket :: Basket -> Basket addToBasket b = let i = item b i' = case i of FreeToChange f -> FreeToChange {freeToChangeCount = f + 1} CannotChange f -> error ("This operation is not allowed") in b {item=i'}
Here are my questions:
1. Is there a way to design the above data structures in such a way I could use the type system. 2. Since, these are runtime changes, is it even a good design pattern to push this responsibility to a type system?
Thanks Guru
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Beware: if you go with Either and combine Either ops with >>=, first Left result stops evaluation and returns 1st Left result. Right results continue evaluation. If this is expected, Either fits. If more ops are expected after 'update count' no-op (if Left stands for Frozen), then Either and >>= should not be used together. Algebraic type (Flex count | Frozen count) is also suitable. Let the calling functions decide how to process Frozen values. At least they'll know the difference.

This sounds like a perfect opportunity to use phantom types. I'll be using it's DataKind-enhanced variant for added beauty.
-- An Item that can be of two types, one whose value can be changed, one whose value are frozen once created data Item = FreeToChange {freeToChangeCount:: Int} | CannotChange {frozenCount:: Int}
I assume all items are equal apart from their changeability. Not that it's necessary, but it makes the demonstration simpler. If changeable and unchangeable items have differing structure you may need additional tools like smart constructors. Accordingly, my items have the type data PlainItem = PlainItem { count :: Int } Changeability will be added on top: data Changeability = Changeable | Unchangeable data Item (c :: Changeability) = Item { plainItem :: PlainItem } Why separate /Item/ and /PlainItem/? One second, please.
-- The item is part of a basket data Basket = Basket { name:: String, item::Item }
data Basket c = Basket { name :: String, item :: Item c } -- No kind signature necessary. Thanks, solver.
Therefore, valid operation are:
1. I can create an Basket with either FreeToChange item or CannotChange item.
The new /Basket/ constructor can do that by default.
2. I can update the count for FreeToChange item in the Basket
changeItem :: (PlainItem -> PlainItem) -> Item 'Changeable -> Item 'Changeable changeItem f (Item i) = Item (f i) changeBasket :: (PlainItem -> PlainItem) -> Basket 'Changeable -> Basket 'Changeable changeBasket f basket@Basket{..} = basket { item = changeItem f item } And that's why /PlainItem/ was separated, so we can have a simple type signature here. You might worry that it was exposed, but it will not give anyone access to a frozen basket. And of course you are free to further restrict access to it. And as we're speaking about freezing, that's extremely simple as well. freezeItem :: Item c -> Item 'Unchangeable freezeItem (Item i) = Item i freezeBasket :: Basket c -> Basket 'Unchangeable freezeBasket basket@Basket{..} = basket { item = freezeItem item } You later mention that you might want to map updates only over some of the baskets in a cart. That's not hard either. As an example, here's a way to implement a function that updates changeable baskets while ignoring unchangeable ones: class MaybeUpdateBasket c where updateBasket :: (PlainItem -> PlainItem) -> Basket c -> Basket c instance MaybeUpdateBasket 'Changeable where updateBasket = changeBasket instance MaybeUpdateBasket 'Unchangeable where updateBasket _ = id Just /map/ it over your cart as always. If you want more complicated things (eg. you want a cart to freeze once any bucket freezes) you just have to expand the ideas here. You may need MultiParamTypeClasses and FunctionalDependencies, but the basic ideas are the same. Cheers.

@Marlin Thank you for the example. Looks very interesting. Initially, when I was modelling this problem, I did consider Phanton types. But, you do provide a nice extension using type classes. Here are the obstacles/discomfort I faced while I took the Phantom Polymorphism approach: 1. With the definition of Basket c, how do I create a type such as Cart = [Basket]. The only way to create such a cart is Cart c = [Basket c]. But, that ties the Cart to one definition of Basket. Is there a way around this? I might be missing something simple. 2. In your approach or in the approach suggested by others, ultimately, I end up handling the 'Frozen' type during run-time. There is no way from stopping somene write code that calls update's on Frozen. (For example while mapping..). Is that understanding correct? Does this approach give me any more power than the previous approaches? The one power is that we stop the user from being able to construct the Frozen type, and we leave it to the compiler to return that type based on the inference. Correct? Is there any other power. Thank you for the help. Just with this thread, I have learnt more than 3 different ways of modelling approaches for this kind of a problem! On Sun, Jul 31, 2016 at 11:41 AM, MarLinn via Haskell-Cafe < haskell-cafe@haskell.org> wrote:
This sounds like a perfect opportunity to use phantom types. I'll be using it's DataKind-enhanced variant for added beauty.
-- An Item that can be of two types, one whose value can be changed, one whose value are frozen once created data Item = FreeToChange {freeToChangeCount:: Int} | CannotChange {frozenCount:: Int}
I assume all items are equal apart from their changeability. Not that it's necessary, but it makes the demonstration simpler. If changeable and unchangeable items have differing structure you may need additional tools like smart constructors. Accordingly, my items have the type
data PlainItem = PlainItem { count :: Int }
Changeability will be added on top:
data Changeability = Changeable | Unchangeable
data Item (c :: Changeability) = Item { plainItem :: PlainItem }
Why separate *Item* and *PlainItem*? One second, please.
-- The item is part of a basket data Basket = Basket { name:: String, item::Item }
data Basket c = Basket { name :: String, item :: Item c } -- No kind signature necessary. Thanks, solver.
Therefore, valid operation are:
1. I can create an Basket with either FreeToChange item or CannotChange item.
The new *Basket* constructor can do that by default.
2. I can update the count for FreeToChange item in the Basket
changeItem :: (PlainItem -> PlainItem) -> Item 'Changeable -> Item 'Changeable
changeItem f (Item i) = Item (f i)
changeBasket :: (PlainItem -> PlainItem) -> Basket 'Changeable -> Basket 'Changeable
changeBasket f basket@Basket{..} = basket { item = changeItem f item }
And that's why *PlainItem* was separated, so we can have a simple type signature here. You might worry that it was exposed, but it will not give anyone access to a frozen basket. And of course you are free to further restrict access to it. And as we're speaking about freezing, that's extremely simple as well.
freezeItem :: Item c -> Item 'Unchangeable
freezeItem (Item i) = Item i
freezeBasket :: Basket c -> Basket 'Unchangeable
freezeBasket basket@Basket{..} = basket { item = freezeItem item }
You later mention that you might want to map updates only over some of the baskets in a cart. That's not hard either. As an example, here's a way to implement a function that updates changeable baskets while ignoring unchangeable ones:
class MaybeUpdateBasket c where updateBasket :: (PlainItem -> PlainItem) -> Basket c -> Basket c instance MaybeUpdateBasket 'Changeable where updateBasket = changeBasket instance MaybeUpdateBasket 'Unchangeable where updateBasket _ = id
Just *map* it over your cart as always.
If you want more complicated things (eg. you want a cart to freeze once any bucket freezes) you just have to expand the ideas here. You may need MultiParamTypeClasses and FunctionalDependencies, but the basic ideas are the same.
Cheers.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

Please excuse me an off-topic question, *MarLinn* can you say what apostrophe means in `Item 'Changeable`? ср, 3 авг. 2016 г. в 20:00, MarLinn via Haskell-Cafe < haskell-cafe@haskell.org>:
I'll try to address the questions in the reverse order.
Does this approach give me any more power than the previous approaches? The one power is that we stop the user from being able to construct the Frozen type, and we leave it to the compiler to return that type based on the inference. Correct? Is there any other power.
The main power lies in the minimalism. You don't need special handling of an *Either* or manual tracking because the types do everything you need.
In your approach or in the approach suggested by others, ultimately, I end up handling the 'Frozen' type during run-time.
Actually, not really. That's the beauty of phantom types: It's almost exclusively a development-time tool that vanishes once the program is compiled. You can't handle the Frozen type during run-time because it will have been dropped - unless you use tricks to keep it, of course. It might feel like you're dealing with the type, but it's all in your head.
There is no way from stopping somene write code that calls update's on Frozen. (For example while mapping..). Is that understanding correct?
Here's my *Item* type again for reference:
data Item (c :: Changeability) = Item { plainItem :: PlainItem }
If you don't export the *Item* constructor, you have full control over who gets to do what with your items. If your only exported update function is
changeItem :: (PlainItem -> PlainItem) -> Item 'Changeable -> Item 'Changeable
then only non-frozen items can be changed, as the type signature says. Of course you have to be careful, e.g. you wouldn't want to export a function like
asChangableItem :: PlainItem -> Item 'Changeable
because then somebody could "unfreeze" items. But as long as you watch your types and exports, you should be fine. (apart from unsafe casting and the like, naturally)
1. With the definition of Basket c, how do I create a type such as
Cart = [Basket]. The only way to create such a cart is Cart c = [Basket c]. But, that ties the Cart to one definition of Basket. Is there a way around this? I might be missing something simple.
That depends. How does a cart behave, depending on the changeability of its contents?
-
*If a cart with any frozen value behaves differently from a cart with only changeable baskets* then add a phantom type to the cart as well. Control the channels into it and have the cart type change depending on what you insert. -
*If all carts behave the same and contents don't matter* just store *PlainBasket*s, i.e. a *Basket* without the annotation. -
*If you want to be able to map over baskets in a cart while handling frozen ones** differently*, then you need both run-time information about basket changeability and a way to share functions between the two different types. The canonical way to share a function between types is a class:
class IsBasket b instance IsBasket (Basket c) type Cart = (IsBasket b) => [b] -- not a good definition, but you get the point
Now *Basket*s can share functions but behave differently. Of course once you need such a class anyway you could ask why you would not want to use it exclusively instead of the phantom types. It's a matter of taste, I guess.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

It's a data constructor promoted to be a type. Similarly the type can
be promoted to become a kind. See also [1].
Erik
[1] https://downloads.haskell.org/~ghc/7.10.3/docs/html/users_guide/promotion.ht...
On 4 August 2016 at 09:36, Geraldus
Please excuse me an off-topic question, MarLinn can you say what apostrophe means in `Item 'Changeable`?
ср, 3 авг. 2016 г. в 20:00, MarLinn via Haskell-Cafe
: I'll try to address the questions in the reverse order.
Does this approach give me any more power than the previous approaches? The one power is that we stop the user from being able to construct the Frozen type, and we leave it to the compiler to return that type based on the inference. Correct? Is there any other power.
The main power lies in the minimalism. You don't need special handling of an Either or manual tracking because the types do everything you need.
In your approach or in the approach suggested by others, ultimately, I end up handling the 'Frozen' type during run-time.
Actually, not really. That's the beauty of phantom types: It's almost exclusively a development-time tool that vanishes once the program is compiled. You can't handle the Frozen type during run-time because it will have been dropped - unless you use tricks to keep it, of course. It might feel like you're dealing with the type, but it's all in your head.
There is no way from stopping somene write code that calls update's on Frozen. (For example while mapping..). Is that understanding correct?
Here's my Item type again for reference:
data Item (c :: Changeability) = Item { plainItem :: PlainItem }
If you don't export the Item constructor, you have full control over who gets to do what with your items. If your only exported update function is
changeItem :: (PlainItem -> PlainItem) -> Item 'Changeable -> Item 'Changeable
then only non-frozen items can be changed, as the type signature says. Of course you have to be careful, e.g. you wouldn't want to export a function like
asChangableItem :: PlainItem -> Item 'Changeable
because then somebody could "unfreeze" items. But as long as you watch your types and exports, you should be fine. (apart from unsafe casting and the like, naturally)
1. With the definition of Basket c, how do I create a type such as
Cart = [Basket]. The only way to create such a cart is Cart c = [Basket c]. But, that ties the Cart to one definition of Basket. Is there a way around this? I might be missing something simple.
That depends. How does a cart behave, depending on the changeability of its contents?
If a cart with any frozen value behaves differently from a cart with only changeable baskets then add a phantom type to the cart as well. Control the channels into it and have the cart type change depending on what you insert.
If all carts behave the same and contents don't matter just store PlainBaskets, i.e. a Basket without the annotation.
If you want to be able to map over baskets in a cart while handling frozen ones differently, then you need both run-time information about basket changeability and a way to share functions between the two different types. The canonical way to share a function between types is a class:
class IsBasket b instance IsBasket (Basket c) type Cart = (IsBasket b) => [b] -- not a good definition, but you get the point
Now Baskets can share functions but behave differently. Of course once you need such a class anyway you could ask why you would not want to use it exclusively instead of the phantom types. It's a matter of taste, I guess.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On Wed, Aug 3, 2016 at 7:58 AM, MarLinn via Haskell-Cafe < haskell-cafe@haskell.org> wrote:
*If all carts behave the same and contents don't matter* just store *PlainBasket*s, i.e. a *Basket* without the annotation.
@MarLinn, Can you clarify the quoted statement. If I were to make store PlainBaskets in the Cart, where would be annotating the Item as Changeble/Unchangable? The way I understand this, you will have to pass their phatom type all the way from the top to the innermost field where it is required. Would the design still hold, if we just declare Baskets of Plain types? I appreciate your response, so far your design make sense. But, I am afraid I am not able to extend it to higher container types.

/If all carts behave the same and contents don't matter/ just store /PlainBasket/s, i.e. a /Basket/ without the annotation.
Can you clarify the quoted statement. If I were to make store PlainBaskets in the Cart, where would be annotating the Item as Changeble/Unchangable?
Well /contents don't matter, /so there is no annotation once you enter a cart. -- A/PlainBasket/ is to a/Basket/ as a/PlainItem/ is to an/Item./ data PlainBasket = PlainBasket { plainBasketName :: String, plainBasketItem :: PlainItem } data Basket c = Basket { basketName :: String, basketItem :: Item c } newtype Cart = Cart [PlainBasket] addBasketToCart :: Basket c -> Cart -> Cart addBasketToCart Basket{..} (Cart cart) = Cart $ newPlainBasket:cart where newPlainBasket = PlainBasket { plainBasketName = basketName , plainBasketItem = plainItem basketItem } The newtype protects your cart from arbitrary changes, so frozen baskets are still protected. But the annotations are discarded. So naturally, you can not recover changeability and you shouldn't allow arbitrary mapping. If you need one of these, this is not the right option. It really depends on what you actually need. Of course you could add a changeability field to /PlainBasket/s and thus store the information at runtime again. But Theodores /Either/-approach or variations of the class I propose in my the third option are more elegant forms of that.
participants (7)
-
Erik Hesselink
-
Geraldus
-
Guru Devanla
-
Gurudev Devanla
-
Imants Cekusins
-
MarLinn
-
Theodore Lief Gannon