How to nest arbitrary things

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
19 messages Options
Reply | Threaded
Open this post in threaded view
|

How to nest arbitrary things

Martin Drautzburg
Hello all,

I was trying to model things which can contain other things. This is easy as long as containers and the contained items
all can be described in the same fashion. However when I want to model - say -

        trucks
        containing boxes
                containing parcels
                        containing cans

and trucks, boxes, parcels and cans are not of the same type, then this nested thing will become a type in its own right
and it will be of a different type than trucks containing cans (which are not wrappen in parcels ...)

As long as I can squeeze trucks, boxes ... into one type, possibly by using a "container_type" attribute, there is no
problem. Is this the only way to do this? Is there an idiom?

_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Magnus Therning

martin writes:

> Hello all,
>
> I was trying to model things which can contain other things. This is
> easy as long as containers and the contained items all can be
> described in the same fashion. However when I want to model - say -
>
> trucks
> containing boxes
> containing parcels
> containing cans
>
> and trucks, boxes, parcels and cans are not of the same type, then
> this nested thing will become a type in its own right and it will be
> of a different type than trucks containing cans (which are not wrappen
> in parcels ...)
>
> As long as I can squeeze trucks, boxes ... into one type, possibly by
> using a "container_type" attribute, there is no problem. Is this the
> only way to do this? Is there an idiom?
Well, you can always make Truck a bit generic:

    data Truck a = Truck [a]

Then you have have a truck of boxes (`Truck Box`) or a truck of cans
(`Truck Can`).

But maybe that's not really your question?

/M

--
Magnus Therning              OpenPGP: 0x927912051716CE39
email: [hidden email]   jabber: [hidden email]
twitter: magthe               http://therning.org/magnus

Any sufficiently advanced technology is indistinguishable from a rigged
demo.
     -- Andy Finkel

_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

signature.asc (815 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Imants Cekusins
>  trucks
        containing boxes
                containing parcels
                        containing cans


.. or try this type:

data Item a = Truck a [Item] | Box a [Item] | Parcel a [Item] | Can a [Item]

"a" here is a property you may use to identify each Item e.g. String
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Frerich Raabe
On 2015-12-21 14:51, Imants Cekusins wrote:
>>  trucks
>         containing boxes
>                 containing parcels
>                         containing cans
>
>
> .. or try this type:
>
> data Item a = Truck a [Item] | Box a [Item] | Parcel a [Item] | Can a [Item]

I guess you'd need that type if you want to be able to express

   
http://i.telegraph.co.uk/multimedia/archive/01845/truck-on-truck-on-_1845173i.jpg

--
Frerich Raabe - [hidden email]
www.froglogic.com - Multi-Platform GUI Testing
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Kim-Ee Yeoh
Administrator
In reply to this post by Imants Cekusins

On Mon, Dec 21, 2015 at 8:51 PM, Imants Cekusins <[hidden email]> wrote:

data Item a = Truck a [Item] | Box a [Item] | Parcel a [Item] | Can a [Item]

A cardinal rule of FP data modelling is to avoid the hazards of junk, a.k.a. make the meaningless unspeakable.

Here an Item could be a Can that contains a Parcel that contains a Box that in turn has a Truck inside.

What will Item-processing functions do with this Item?

Garbage In Garbage Out.

-- Kim-Ee

_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Imants Cekusins
In reply to this post by Frerich Raabe
>  http://i.telegraph.co.uk/multimedia/archive/01845/truck-on-truck-on-_1845173i.jpg

:D

> Garbage In Garbage Out.


well Martin would like to
>  model things which can contain other things.
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

David McBride
In reply to this post by Martin Drautzburg
Personally I'd try to use the type system, if possible.

data Box a = Box [a]
data Parcel a = Parcel [a]
data Can = Can


data Truck a = Truck {
  tname :: String,
  truckContents :: [a]
}

oneboxoftwocanstruck :: Truck (Box Can)
oneboxoftwocanstruck = Truck "Truck of Boxes of Cans" [Box [Can,Can]]

onecantruck :: Truck Can
onecantruck = Truck "Truck of Cans" [Can]

This gets some of the type safety without bogging you down too much.  You can still end up with parcels with trucks in them, but it's not too bad.  At least cans are just cans, and functions can be written for trucks that only work on trucks, for example.

On Mon, Dec 21, 2015 at 4:40 AM, martin <[hidden email]> wrote:
Hello all,

I was trying to model things which can contain other things. This is easy as long as containers and the contained items
all can be described in the same fashion. However when I want to model - say -

        trucks
        containing boxes
                containing parcels
                        containing cans

and trucks, boxes, parcels and cans are not of the same type, then this nested thing will become a type in its own right
and it will be of a different type than trucks containing cans (which are not wrappen in parcels ...)

As long as I can squeeze trucks, boxes ... into one type, possibly by using a "container_type" attribute, there is no
problem. Is this the only way to do this? Is there an idiom?

_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners


_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Frerich Raabe
In reply to this post by Martin Drautzburg
On 2015-12-21 10:40, martin wrote:

> I was trying to model things which can contain other things. This is easy as
> long as containers and the contained items
> all can be described in the same fashion. However when I want to model - say
> -
>
> trucks
> containing boxes
> containing parcels
> containing cans
>
> and trucks, boxes, parcels and cans are not of the same type, then this
> nested thing will become a type in its own right
> and it will be of a different type than trucks containing cans (which are
> not wrappen in parcels ...)
>
> As long as I can squeeze trucks, boxes ... into one type, possibly by using
> a "container_type" attribute, there is no
> problem. Is this the only way to do this? Is there an idiom?

In addition to what the others wrote (I'd personally probably start with the
'data Truck a = Truck [a]' idea) you could also use the type system to
express the possible *legal* ways to nest the load of a truck, e.g.:

   -- A Can can't contain anything
   data Can = Can

   -- A Parcel consists of zero or more cans
   data Parcel = Parcel [Can]

   -- A Box can be empty or contain a mixture of cans and parcels
   data BoxContent = BCCan Can | BCParcel Parcel
   data Box = Box [BoxContent]

   -- A Truck can be empty or contain a mixture of cans, parcels and boxes
   data TruckConent = TCCan Can | TCParcel Parcel | TCBox Box
   data Truck = Truck [TruckContent]

This might be too annoying to deal with though, i.e. the gain of type safety
is not big enough to actually follow this path.

--
Frerich Raabe - [hidden email]
www.froglogic.com - Multi-Platform GUI Testing
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Martin Drautzburg
Am 12/21/2015 um 03:21 PM schrieb Frerich Raabe:

> On 2015-12-21 10:40, martin wrote:
>> I was trying to model things which can contain other things. This is easy as long as containers and the contained items
>> all can be described in the same fashion. However when I want to model - say -
>>
>>     trucks
>>     containing boxes
>>         containing parcels
>>             containing cans
>>
>> and trucks, boxes, parcels and cans are not of the same type, then this nested thing will become a type in its own right
>> and it will be of a different type than trucks containing cans (which are not wrappen in parcels ...)
>>
>> As long as I can squeeze trucks, boxes ... into one type, possibly by using a "container_type" attribute, there is no
>> problem. Is this the only way to do this? Is there an idiom?
>
> In addition to what the others wrote (I'd personally probably start with the 'data Truck a = Truck [a]' idea) you could
> also use the type system to express the possible *legal* ways to nest the load of a truck, e.g.:
>
>   -- A Can can't contain anything
>   data Can = Can
>
>   -- A Parcel consists of zero or more cans
>   data Parcel = Parcel [Can]
>
>   -- A Box can be empty or contain a mixture of cans and parcels
>   data BoxContent = BCCan Can | BCParcel Parcel
>   data Box = Box [BoxContent]
>
>   -- A Truck can be empty or contain a mixture of cans, parcels and boxes
>   data TruckConent = TCCan Can | TCParcel Parcel | TCBox Box
>   data Truck = Truck [TruckContent]
>
> This might be too annoying to deal with though, i.e. the gain of type safety is not big enough to actually follow this
> path.
>

That's fine. I'm happy not to be able to pack a truck into another truck. Only problem is that I don't know how to write
an "unpack" function, which removes one level of nesting. I can only write unpackTruck, unpackParcel ...

I suppose the ability to write a generic unpack function implies that there can be a generic pack function and then I
could pack a truck into another truck.






_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Francesco Ariis
On Mon, Dec 21, 2015 at 06:00:06PM +0100, martin wrote:
> That's fine. I'm happy not to be able to pack a truck into another truck.
> Only problem is that I don't know how to write an "unpack" function,
> which removes one level of nesting. I can only write unpackTruck,
> unpackParcel ...
>
> I suppose the ability to write a generic unpack function implies that
> there can be a generic pack function and then I could pack a truck into
> another truck.

I would say typeclasses might help you, but before that, what would
the unpack function signature look like?

    unpack :: (Package s) => s a -> [a]

Like this? If so, I don't see much benefit (or what problem we're
trying to solve) in trucks>boxes>parcels>cans types.



_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Imants Cekusins
In reply to this post by Martin Drautzburg
> how to write an "unpack" function

is this what Lenses are all about?

https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/a-little-lens-starter-
tutorial

I stumbled across Lenses only recently so can not write a working example yet.

if Lenses are relevant to this topic, could someone kindly post an
appropriate (Truck Parcel Can) datatype and an "unpack" with Lenses
example?
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Daniel Trstenjak-2
On Mon, Dec 21, 2015 at 06:47:54PM +0100, Imants Cekusins wrote:
> if Lenses are relevant to this topic, could someone kindly post an
> appropriate (Truck Parcel Can) datatype and an "unpack" with Lenses
> example?

For a beginner lenses might be a bit too much, because their error
messages can be quite hairy, so I most likely would suggest not
using them for the moment.

Otherwise modifying data in a deeply nested data structure without
lenses can be such a pain, so here's an example.

Taking the example from Frerich and making it a bit easier usable with lenses:
 
   {-# Language TemplateHaskell #-}
   import Control.Lens

  data Can = Can deriving Show

  data Parcel = Parcel { _cans :: [Can] } deriving Show
  makeLenses ''Parcel
 
  data BoxContent = BCCan Can | BCParcel Parcel deriving Show
  makePrisms ''BoxContent
 
  data Box = Box { _boxContents :: [BoxContent] } deriving Show
  makeLenses ''Box
 
  data TruckContent = TCCan Can | TCParcel Parcel | TCBox Box deriving Show
  makePrisms ''TruckContent
 
  data Truck = Truck { _truckContents :: [TruckContent] } deriving Show
  makeLenses ''Truck


By default 'makeLenses' will make lenses for every field in the record
prefixed with '_', so e.g. for the field '_cans' of the record 'Parcel'
a lens with the name 'cans' will be created.

For ADTs like 'BoxContent' a "special" kind of lens is created - called
prism - by calling 'makePrisms'. For every data constructor of
'BoxContent' - in this case 'BCCan' and 'BCParcel' - a prism with the
name of the data constructor prefixed with a '_' is created: '_BCCan'
and '_BCParcel'.


Now put the whole above Haskell code into a file like 'Main.hs'.
If you have already 'cabal' installed, then installing the
'lens' library into a sandbox and opening a repl with the
'Main.hs' file is one way of testing it:

   ~> cabal sandbox init
   ~> cabal install lens
   ~> cabal repl

And now calling inside of the repl:

   :load Main.hs


Creating a truck with some contents:

   let truck = Truck [TCBox (Box [BCCan Can])]


Looking at the contents of the truck:

   truck ^. truckContents

You can read the '^.' as applying the lens 'truckContents'
on the variable 'truck'. It has the same effect as
calling the field accessor '_truckContents' on 'truck'.

   _truckContents truck


Now you can go deeper:

   truck ^.. truckContents . traverse . _TCBox

This already involves two new things '^..' and 'traverse'.
'traverse' does visit every 'TruckContent' in 'truckContents',
so it's in lens speak a traversal and because it might give
multiple results you need the '^..', which collects all
results into a list.

The '_TCBox' works like a filter, so you're collecting all
'TCBox' of all 'TruckContent'. Try it with '_TCParcel'.


Now you can go even deeper:

   truck ^.. truckContents . traverse . _TCBox . boxContents . traverse . _BCCan


Until now you only viewed the data, but if you want to modify the data
you need the operators '&', '.~' and '~%'.

For e.g. to clear (setting an empty list) the 'boxContents' of every 'TCBox':

   truck & truckContents . traverse . _TCBox . boxContents .~ []


Or modifying the 'boxContents' by adding a 'BCCan':

   truck & truckContents . traverse . _TCBox . boxContents %~ (BCCan Can :)

This could be also written as:

   truck & truckContents . traverse . _TCBox . boxContents %~ (\contents -> BCCan Can : contents)



This is only the tip of the iceberg regarding lenses, but with
'makeLenses', 'makePrisms', '^.', '^..', '.~', '&' and '~%' you
can already get quite far.

Hopefully this was a bit helpful and not too much at once. :)

These examples use the lenses from the 'lens'[1] library.


Greetings,
Daniel

[1] https://hackage.haskell.org/package/lens
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Martin Drautzburg
In reply to this post by Francesco Ariis
Am 12/21/2015 um 06:30 PM schrieb Francesco Ariis:

> On Mon, Dec 21, 2015 at 06:00:06PM +0100, martin wrote:
>> That's fine. I'm happy not to be able to pack a truck into another truck.
>> Only problem is that I don't know how to write an "unpack" function,
>> which removes one level of nesting. I can only write unpackTruck,
>> unpackParcel ...
>>
>> I suppose the ability to write a generic unpack function implies that
>> there can be a generic pack function and then I could pack a truck into
>> another truck.
>
> I would say typeclasses might help you, but before that, what would
> the unpack function signature look like?
>
>     unpack :: (Package s) => s a -> [a]
>
> Like this? If so, I don't see much benefit (or what problem we're
> trying to solve) in trucks>boxes>parcels>cans types.

Unpacking should separate the container from its contents, i.e. given a packed container it should return an empty
container and whatever was inside.

_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Imants Cekusins
In reply to this post by Daniel Trstenjak-2

> a lens with the name 'cans' will be created.

Does Template Haskell modify code file or can these '_' names be used throughout the code without visible definition?

Thank you for this very detailed and specific example.

Could we say that lens is a bit like extension to record syntax: allow to traverse, get and set properties for structures that are  more complex than simple 1 level record?

Why do lens require Template Haskell? Only to generate '_' functions?


_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Francesco Ariis
In reply to this post by Martin Drautzburg
On Mon, Dec 21, 2015 at 08:41:47PM +0100, martin wrote:

> > I would say typeclasses might help you, but before that, what would
> > the unpack function signature look like?
> >
> >     unpack :: (Package s) => s a -> [a]
> >
> > Like this? If so, I don't see much benefit (or what problem we're
> > trying to solve) in trucks>boxes>parcels>cans types.
>
> Unpacking should separate the container from its contents, i.e.
> given a packed container it should return an empty container and
> whatever was inside.

I'd still ask for a type signature if you feel it's possible, it
clears things up (and/or highlights where the type system is getting
in the way).

An I'd still argue that "arbitrarily nestable" things is a bad idea,
as Kim-Ee Yeoh explained. I like Haskell type system because carefully
designed types "lead the way": some 'wrong' code won't even compile.

In real life, what are we trying to model? Why is `unpack` useful/needed?
How would I use its output? (a valid answer being: "just a mental
experiment")

_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Imants Cekusins
In reply to this post by Daniel Trstenjak-2
> makeLenses ''Parcel

any cues re: what '' stand for in the above expression?
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Daniel Trstenjak-2
In reply to this post by Imants Cekusins
On Mon, Dec 21, 2015 at 09:06:55PM +0100, Imants Cekusins wrote:
> > a lens with the name 'cans' will be created.
>
> Does Template Haskell modify code file or can these '_' names be used
> throughout the code without visible definition?

In this case Template Haskell doesn't modify the code, fields like
'_cans' are just normal fields and can be used with this name.

There's only the convention that 'makeLenses' creates for every field
with a '_' prefix a lens.

In the case of '_cans' a lens like this will be created:

   cans :: Lens' Parcel [Can]
   cans = lens getCans setCans
      where
         getCans parcel      = _cans parcel
         setCans parcel cans = parcel { _cans = cans }


(This is just an idealized implementation and the real one
will be most likely a bit more sophisticated and optimized.)

> Could we say that lens is a bit like extension to record syntax: allow to
> traverse, get and set properties for structures that are  more complex than
> simple 1 level record?

It's often compared with some kind of jQuery for data structures.

> Why do lens require Template Haskell? Only to generate '_' functions?

To automatically generate the Haskell code like the lens 'cans' in the
above example.


Greetings,
Daniel
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Daniel Trstenjak-2
In reply to this post by Imants Cekusins
On Mon, Dec 21, 2015 at 09:56:04PM +0100, Imants Cekusins wrote:
> > makeLenses ''Parcel
>
> any cues re: what '' stand for in the above expression?

It's just the escaping of the name needed for Template
Haskell. I think it's the same like calling:

   makeLenses "Parcel"
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: How to nest arbitrary things

Imants Cekusins
Thank you very much Daniel. Lens is a lot clearer now.

jQuery eh?


 $(truckContents . _TCBox)

;)
_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners