Type synonyms vs standard types

Hi everyone, Dumb question about declaring a function and type synonyms. There are to different declarations of the same function: attrNames :: String -> AttrDict -> [String] attrNames :: AttrClass -> AttrDict -> AttrNames First gives you the idea about exact types it expects (except AttrDict for which user has to take a look into the docs or sources) while the second one gives you the idea about meaning of parameters. Both reasons make sense. The question is when which one should be used? I'm using type synonyms everywhere and possibly without too much reasons... Maybe I should stop doing it? :) Cheers, Oleksandr.

On Tue, Sep 29, 2009 at 7:40 PM, Olex P
Hi everyone,
Dumb question about declaring a function and type synonyms. There are to different declarations of the same function:
attrNames :: String -> AttrDict -> [String]
attrNames :: AttrClass -> AttrDict -> AttrNames
First gives you the idea about exact types it expects (except AttrDict for which user has to take a look into the docs or sources) while the second one gives you the idea about meaning of parameters. Both reasons make sense. The question is when which one should be used? I'm using type synonyms everywhere and possibly without too much reasons... Maybe I should stop doing it? :)
I like type synonyms. You can always look up what the type synonym boils down to, while the reverse cannot be done. Of course, some Haddock markup can also solve the problem. Michael

On Tue, Sep 29, 2009 at 11:40 AM, Olex P
Hi everyone,
Dumb question about declaring a function and type synonyms. There are to different declarations of the same function:
attrNames :: String -> AttrDict -> [String]
attrNames :: AttrClass -> AttrDict -> AttrNames
First gives you the idea about exact types it expects (except AttrDict for which user has to take a look into the docs or sources) while the second one gives you the idea about meaning of parameters. Both reasons make sense. The question is when which one should be used? I'm using type synonyms everywhere and possibly without too much reasons... Maybe I should stop doing it? :)
I get the impression that it is still largely a matter of style, and the community hasn't reached a consensus. Personally, I don't usually use type synonyms for this purpose. The function name and type are usually enough. Eg in attrNames above it should be clear what's going on: you are taking one AttrDict and one String, so the String is probably a key into the dictionary. The return is a list of Strings, and your function is called "attrNames", so it's probably a list of attr names. In the cases when it is not as obvious, I usually increase the level of abstraction (typechecked documentation) instead of introducing synonyms (un-typechecked documentation). Take for example the function which checks whether a point is inside a bounding box: insideBoundingBox :: Point -> Point -> Point -> Bool I could use synonyms to rename those arguments: insideBoundingBox :: LowerLeftPoint -> UpperRightPoint -> Point -> Bool But I would prefer to introduce a new abstraction: data BoundingBox = BoundingBox { lowerLeft :: Point, upperRight :: Point } insideBoundingBox :: BoundingBox -> Point -> Bool And now the roles of the arguments are clear again. Luke

This idea with new level of abstraction is good but in some cases it can
make things overcomplicated / less efficient. Does that mean "leave simple
built-in types as is"?
But probably that's all is the matter of habit and style.
Thanks guys
On Tue, Sep 29, 2009 at 8:58 PM, Luke Palmer
On Tue, Sep 29, 2009 at 11:40 AM, Olex P
wrote: Hi everyone,
Dumb question about declaring a function and type synonyms. There are to different declarations of the same function:
attrNames :: String -> AttrDict -> [String]
attrNames :: AttrClass -> AttrDict -> AttrNames
First gives you the idea about exact types it expects (except AttrDict for which user has to take a look into the docs or sources) while the second one gives you the idea about meaning of parameters. Both reasons make sense. The question is when which one should be used? I'm using type synonyms everywhere and possibly without too much reasons... Maybe I should stop doing it? :)
I get the impression that it is still largely a matter of style, and the community hasn't reached a consensus.
Personally, I don't usually use type synonyms for this purpose. The function name and type are usually enough. Eg in attrNames above it should be clear what's going on: you are taking one AttrDict and one String, so the String is probably a key into the dictionary. The return is a list of Strings, and your function is called "attrNames", so it's probably a list of attr names.
In the cases when it is not as obvious, I usually increase the level of abstraction (typechecked documentation) instead of introducing synonyms (un-typechecked documentation). Take for example the function which checks whether a point is inside a bounding box:
insideBoundingBox :: Point -> Point -> Point -> Bool
I could use synonyms to rename those arguments:
insideBoundingBox :: LowerLeftPoint -> UpperRightPoint -> Point -> Bool
But I would prefer to introduce a new abstraction:
data BoundingBox = BoundingBox { lowerLeft :: Point, upperRight :: Point } insideBoundingBox :: BoundingBox -> Point -> Bool
And now the roles of the arguments are clear again.
Luke

On Tue, Sep 29, 2009 at 2:21 PM, Olex P
This idea with new level of abstraction is good but in some cases it can make things overcomplicated / less efficient. Does that mean "leave simple built-in types as is"? But probably that's all is the matter of habit and style.
Well as far as efficiency, these are typically micro-considerations -- you're talking in terms of cycles. Cross those bridges when you come to them. As with overcomplication, the story is the same as with all abstraction: it's a fine balance between how much work they are to declare, their cognitive benefit, and how many times they are used. Haskell has wonderfully lightweight declarations, so that trade-off favors the new abstraction much more quickly than in other languages. Luke

Olex P wrote:
This idea with new level of abstraction is good but in some cases it can make things overcomplicated / less efficient. Does that mean "leave simple built-in types as is"?
That's what newtypes are for. A newtype is like a type alias, except that it is type checked. All newtype wrappering/unwrappering is compiled away so the representations are the same. The only performance difference is (== should be) that there can be overhead for strange ways of providing typeclass instances.[1] [1] By "strange ways" of providing typeclass instances I mean things like using class Peano p where ... data Z = Z newtype S n = S n instance Peano Z where ... instance Peano n => Peano (S n) where ... instead of a straightforward data ZorS = Z | S Peano instance Peano ZorS where ... Because of the newtype, the representation of (S n) is the same as the representation of Z, thus all peano numbers are the same size. However, we still need to keep that info around somewhere, and consequently the size of the (Peano n) dictionary is linear in n (because it needs a pointer to the (Peano (n-1)) dictionary, and so on until (Peano Z)). On the other hand, with the straightforward version, the size of (n :: ZorS) is linear in n, but the size of the (Peano ZorS) dictionary is constant. -- Live well, ~wren

True. But anyway newtype creates a new type which is not what I'm looking
for. In this case instead of passing a string "myAttrName" user should pass
constructor as well. And the next step of such "simplification" will be a
smart constructor attrName? :) And that's all just to show user of that
function what kind of parameters function expects! :-D
On Wed, Sep 30, 2009 at 5:47 AM, wren ng thornton
Olex P wrote:
This idea with new level of abstraction is good but in some cases it can make things overcomplicated / less efficient. Does that mean "leave simple built-in types as is"?
That's what newtypes are for. A newtype is like a type alias, except that it is type checked. All newtype wrappering/unwrappering is compiled away so the representations are the same. The only performance difference is (== should be) that there can be overhead for strange ways of providing typeclass instances.[1]
[1] By "strange ways" of providing typeclass instances I mean things like using
class Peano p where ...
data Z = Z newtype S n = S n instance Peano Z where ... instance Peano n => Peano (S n) where ...
instead of a straightforward
data ZorS = Z | S Peano instance Peano ZorS where ...
Because of the newtype, the representation of (S n) is the same as the representation of Z, thus all peano numbers are the same size. However, we still need to keep that info around somewhere, and consequently the size of the (Peano n) dictionary is linear in n (because it needs a pointer to the (Peano (n-1)) dictionary, and so on until (Peano Z)).
On the other hand, with the straightforward version, the size of (n :: ZorS) is linear in n, but the size of the (Peano ZorS) dictionary is constant.
-- Live well, ~wren _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
participants (4)
-
Luke Palmer
-
Michael Snoyman
-
Olex P
-
wren ng thornton