Can fields in a record be optional?

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

Can fields in a record be optional?

Costello, Roger L.
Hi Folks,

Can fields in a record be made "optional"?

Let me motivate my question:

I have a data type called "Contract". It is a record. It has one field that holds a currency value, a second field that holds payments, and a third field to hold sub-Contracts.

data Contract =  Contract {
                     currency  :: Currency
                   , payments  :: Double
                   , contracts :: [Contract]
                 }
                 deriving (Show)

Here is a function that creates a Contract with currency c and payments = 1:

one    :: Currency -> Contract
one c  =  Contract { currency = c, payments = 1, contracts = [] }

Since there are no sub-Contracts I'd rather not specify that last field.

Here is a function that AND's two Contracts, c1 and c2, and returns one Contract:

and          :: Contract -> Contract -> Contract
(and) c1 c2  =  Contract { currency = undefined, payments = undefined, contracts = [c1, c2] }

I'd rather not specify those first two fields.

See why I desire optional fields?

Perhaps I am going about it completely wrong and there is a better way to express Contract?  I am eager to learn!

/Roger


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

David Place
The way I often do this is to create an "ur" instance where all the fields have default values.  Then to create an instance, I just do a "record update" of this instance.  For example:

data Contract =  Contract {
                    currency  :: Currency
                  , payments  :: Double
                  , contracts :: [Contract]
                }
                deriving (Show)

urContract = Contract { currency = undefined, payments = undefined, contracts = [] }

one    :: Currency -> Contract
one c  =  urContract { currency = c, payments = 1}

and          :: Contract -> Contract -> Contract
(and) c1 c2  =  urContract { contracts = [c1, c2] }

____________________
David Place  
Owner, Panpipes Ho! LLC
http://panpipesho.com
d at vidplace.com



On Jul 17, 2011, at 6:03 PM, Costello, Roger L. wrote:

> Hi Folks,
>
> Can fields in a record be made "optional"?
>



Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Christopher Done
On 18 July 2011 00:18, David Place <d at vidplace.com> wrote:
> The way I often do this is to create an "ur" instance where all the fields have default values. ?Then to create an instance, I just do a "record update" of this instance.

For this there is Data.Default in data-default:

http://hackage.haskell.org/packages/archive/data-default/0.2.0.1/doc/html/Data-Default.html


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

David Virebayre
In reply to this post by Costello, Roger L.
I'm not sure I would model your datatype this way, I don't like the
idea to put unnecessary undefined values in the case of subcontracts.

I would instead

data Contract = Contract  { currency :: Currency, payments :: Double }
              | SubContract { contracts :: [Contract] }

one c  =  Contract { currency = c, payments = 1 }


and          :: Contract -> Contract -> Contract
(and) c1 c2  =  Subcontract { contracts = [c1, c2] }


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Yitzchak Gale
In reply to this post by Christopher Done
Christopher Done wrote:
> For this there is Data.Default in data-default:
> http://hackage.haskell.org/packages/archive/data-default/0.2.0.1/doc/html/Data-Default.html

You don't need the added complexity of a type class for
this - just define the default value.

The case where you need a class for default values is when you're using
generics. See, for example, System.Console.CmdArgs.Default in
Neil Mitchell's cmdards package[1].

Regards,
Yitz

[1] http://hackage.haskell.org/package/cmdargs


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Yitzchak Gale
Oops, sorry about the typo. It's the *cmdargs* package.
http://hackage.haskell.org/package/cmdargs

-Yitz


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Michael Snoyman
In reply to this post by Yitzchak Gale
On Mon, Jul 18, 2011 at 9:44 AM, Yitzchak Gale <gale at sefer.org> wrote:
> Christopher Done wrote:
>> For this there is Data.Default in data-default:
>> http://hackage.haskell.org/packages/archive/data-default/0.2.0.1/doc/html/Data-Default.html
>
> You don't need the added complexity of a type class for
> this - just define the default value.

I don't think it's a case of necessity. It just happens to be very
convenient to be able to use the three letters "def" instead of
"defaultFrobnicatorSettings" or something like that.

Michael

> The case where you need a class for default values is when you're using
> generics. See, for example, System.Console.CmdArgs.Default in
> Neil Mitchell's cmdards package[1].
>
> Regards,
> Yitz
>
> [1] http://hackage.haskell.org/package/cmdargs
>
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://www.haskell.org/mailman/listinfo/beginners
>


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Yitzchak Gale
In reply to this post by David Virebayre
Roger L. Costello wrote:
>> data Contract =
>>   Contract {
>>              currency  :: Currency
>>            , payments  :: Double
>>            , contracts :: [Contract]
>>            }
>>  deriving (Show)

David Virebayre wrote:
> I'm not sure I would model your datatype this way, I don't like the
> idea to put unnecessary undefined values in the case of subcontracts.
>
> data Contract = Contract ?{ currency :: Currency, payments :: Double }
> ? ? ? ? ? ? ?| SubContract { contracts :: [Contract] }

I think Roger's original design is just fine.

There is nothing "undefined" about the empty list. Roger is
saying that every contract has a list of subcontracts. Lists can
be empty, and that is legitimate.

In effect, Roger has defined his collection of contracts to be
a rose tree, and this is the classic way to do that in Haskell.
Another option would be to be more explicit about that and
use Data.Tree from the containers[1] package, which is
included in the Haskell Platform.

Tree algorithms are often quite elegant.
We haven't seen the rest of Roger's program, but I'll
bet it looks really nice with his original definition.

Regards,
Yitz


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

David Virebayre
2011/7/18 Yitzchak Gale <gale at sefer.org>:

> Roger L. Costello wrote:
>>> data Contract =
>>> ? Contract {
>>> ? ? ? ? ? ? ?currency ?:: Currency
>>> ? ? ? ? ? ?, payments ?:: Double
>>> ? ? ? ? ? ?, contracts :: [Contract]
>>> ? ? ? ? ? ?}
>>> ?deriving (Show)
>
> David Virebayre wrote:
>> I'm not sure I would model your datatype this way, I don't like the
>> idea to put unnecessary undefined values in the case of subcontracts.
>>
>> data Contract = Contract ?{ currency :: Currency, payments :: Double }
>> ? ? ? ? ? ? ?| SubContract { contracts :: [Contract] }
>
> I think Roger's original design is just fine.
>
> There is nothing "undefined" about the empty list. Roger is
> saying that every contract has a list of subcontracts. Lists can
> be empty, and that is legitimate.

I'm not sure you read his post entirely.

Here's his and function :

> (and) c1 c2  =  Contract { currency = undefined, payments = undefined, contracts = [c1, c2] }

Notice how he's setting currency and payments to undefined, since he
needs only the contracts field for subcontracts.

David.


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Yitzchak Gale
David Virebayre wrote:
> I'm not sure you read his post entirely.
>
> Here's his and function :
>> (and) c1 c2 ?= ?Contract { currency = undefined, payments = undefined, contracts = [c1, c2] }
>
> Notice how he's setting currency and payments to undefined, since he
> needs only the contracts field for subcontracts.

Ah, right, I didn't notice that. Not a good idea. Your design
is better then.

Another possibility would be Maybe Currency and Maybe Double.
That often comes out simpler - but not always. If the case
with Currency and Payments is a totally separate case than
when there are subcontracts, with different logic, then David's
design is best. If the cases are usually handled pretty much
in the same way with special handling for when one or more
fields is missing, then Maybe is best.

-Yitz


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Yitzchak Gale
In reply to this post by Michael Snoyman
I wrote:
>> You don't need the added complexity of a type class for
>> this - just define the default value.

Michael Snoyman wrote:
> I don't think it's a case of necessity. It just happens to be very
> convenient to be able to use the three letters "def" instead of
> "defaultFrobnicatorSettings" or something like that.

You can use whatever name you'd like for a default value,
short or descriptive.

Type classes are a beautiful and powerful technique
in Haskell, but they do add some underlying complexity
to your types. In my experience, when building and
maintaining large code bases in Haskell, things are
much easier when you limit the use of type classes
to when they are really needed.

I create type classes when I need non-trivial polymorphism.
Even then, I often prefer simpler techniques such
as data types with parameters or passing functions as
parameters. There are also advanced techniques
using type classes which are needed for building some
kinds of libraries.

In particular, I have come to the conclusion that type classes
are not a good tool for simple namespace control in a typical
Haskell program.

It is a common mistake for beginners who come to Haskell
with a background in OOP to massively overuse type
classes.

Style is a matter of taste, of course. But much of the power
and beauty of Haskell lies away from type classes, and it's
a shame to miss out on it. So I think it is important for beginners,
especially when coming from OOP, to start out by avoiding
defining type classes whenever there is a good alternative
(i.e. almost always).

Regards,
Yitz


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

David Place
In reply to this post by Christopher Done
Thanks for the pointer to Data.Default.  I don't see how it applies in this case, though.  The goal is to avoid mentioning the defaulted fields when constructing a record instance.  Example?

____________________
David Place  
Owner, Panpipes Ho! LLC
http://panpipesho.com
d at vidplace.com



On Jul 18, 2011, at 1:45 AM, Christopher Done wrote:

> On 18 July 2011 00:18, David Place <d at vidplace.com> wrote:
>> The way I often do this is to create an "ur" instance where all the fields have default values.  Then to create an instance, I just do a "record update" of this instance.
>
> For this there is Data.Default in data-default:
>
> http://hackage.haskell.org/packages/archive/data-default/0.2.0.1/doc/html/Data-Default.html



Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

austin seipp-3
It's effectively the exact same thing as your example but with the
default record put into a typeclass for convenience. Refactoring your
original post:

data Contract =  Contract {
                   currency  :: Currency
                 , payments  :: Double
                 , contracts :: [Contract]
               }
               deriving (Show)

instance Default Contract where
  def = Contract { currency = undefined, payments = undefined, contracts = [] }

one    :: Currency -> Contract
one c  =  def { currency = c, payments = 1}

and          :: Contract -> Contract -> Contract
(and) c1 c2  =  def { contracts = [c1, c2] }

It's a very simple package and provides some default instances for
your convenience. Use it if you like.

On Mon, Jul 18, 2011 at 3:47 PM, David Place <d at vidplace.com> wrote:

> Thanks for the pointer to Data.Default. ?I don't see how it applies in this case, though. ?The goal is to avoid mentioning the defaulted fields when constructing a record instance. ?Example?
>
> ____________________
> David Place
> Owner, Panpipes Ho! LLC
> http://panpipesho.com
> d at vidplace.com
>
>
>
> On Jul 18, 2011, at 1:45 AM, Christopher Done wrote:
>
>> On 18 July 2011 00:18, David Place <d at vidplace.com> wrote:
>>> The way I often do this is to create an "ur" instance where all the fields have default values. ?Then to create an instance, I just do a "record update" of this instance.
>>
>> For this there is Data.Default in data-default:
>>
>> http://hackage.haskell.org/packages/archive/data-default/0.2.0.1/doc/html/Data-Default.html
>
>
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://www.haskell.org/mailman/listinfo/beginners
>



--
Regards,
Austin


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

David Place
Thanks, Austin, for taking the time to explain.  I guess I don't find it gives any advantage over the way I currently do it.

_____________________
David F. Place
Owner,  Panpipes Ho!, LLC
http://panpipesho.com

On Jul 18, 2011, at 5:33 PM, austin seipp <as at hacks.yi.org> wrote:

> It's effectively the exact same thing as your example but with the
> default record put into a typeclass for convenience.


Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

Mats Rauhala
In reply to this post by David Place
On 18:18 Sun 17 Jul     , David Place wrote:
> The way I often do this is to create an "ur" instance where all the fields have default values.  Then to create an instance, I just do a "record update" of this instance.  For example:
>
> urContract = Contract { currency = undefined, payments = undefined, contracts = [] }

Out of curiosity, what's the etymology for your 'ur'?

--
Mats Rauhala
MasseR
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://www.haskell.org/pipermail/beginners/attachments/20110720/fa0bdf8e/attachment.pgp>

Reply | Threaded
Open this post in threaded view
|

Can fields in a record be optional?

David Place
On Jul 20, 2011, at 8:07 AM, Mats Rauhala wrote:

> On 18:18 Sun 17 Jul     , David Place wrote:
>> The way I often do this is to create an "ur" instance where all the fields have default values.  Then to create an instance, I just do a "record update" of this instance.  For example:
>>
>> urContract = Contract { currency = undefined, payments = undefined, contracts = [] }
>
> Out of curiosity, what's the etymology for your 'ur'?

A prefix denoting the earliest or original form of something.

>
> --
> Mats Rauhala
> MasseR
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://www.haskell.org/mailman/listinfo/beginners