
Newbie question: I was wondering the other day if type synonyms might be more useful if they were more restricted, that is, with the definitions: type Foo = String type Bar = String foo :: Foo foo = "a foo" bar :: Bar bar = "a bar" x :: Foo -> ... x f b = ...only valid for Foo Strings... both 'x foo' and 'x bar' type check correctly. Wouldn't it be useful if Foo and Bar were both equivalent to String, but Foo and Bar were not equivalent themselves? For instance, if you are using Strings as properties of something and want to associate the type of the property with its value, without wrapping the String. Would this break a transitivity property of the type system? Am I just suffering from laziness?

If you change your type declarations to 'newtype' declarations, I
believe you would get the effect that you want, depending on what you
mean by 'equivalent'. In that case, Foo and Bar would essentially be
strings, but you could not use either of them in a place where the
other is expected, nor where a String is expected. See
http://haskell.org/haskellwiki/Newtype for more information. Hope this
helps!
On 10/10/07, Tom Davies
Newbie question:
I was wondering the other day if type synonyms might be more useful if they were more restricted, that is, with the definitions:
type Foo = String type Bar = String
foo :: Foo foo = "a foo"
bar :: Bar bar = "a bar"
x :: Foo -> ... x f b = ...only valid for Foo Strings...
both 'x foo' and 'x bar' type check correctly.
Wouldn't it be useful if Foo and Bar were both equivalent to String, but Foo and Bar were not equivalent themselves?
For instance, if you are using Strings as properties of something and want to associate the type of the property with its value, without wrapping the String.
Would this break a transitivity property of the type system?
Am I just suffering from laziness?
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Andrew Wagner
If you change your type declarations to 'newtype' declarations, I believe you would get the effect that you want, depending on what you mean by 'equivalent'. In that case, Foo and Bar would essentially be strings, but you could not use either of them in a place where the other is expected, nor where a String is expected. See http://haskell.org/haskellwiki/Newtype for more information. Hope this helps!
I wanted to avoid wrapping the string with a constructor. I suppose what I'm really asking for is for each type to implicitly define a 'type class with no methods', and to be able to create new instances of that type class which simply behave as the underlying type.

On Thu, 11 Oct 2007, Tom Davies wrote:
Andrew Wagner
writes: If you change your type declarations to 'newtype' declarations, I believe you would get the effect that you want, depending on what you mean by 'equivalent'. In that case, Foo and Bar would essentially be strings, but you could not use either of them in a place where the other is expected, nor where a String is expected. See http://haskell.org/haskellwiki/Newtype for more information. Hope this helps!
I wanted to avoid wrapping the string with a constructor.
I suppose what I'm really asking for is for each type to implicitly define a 'type class with no methods', and to be able to create new instances of that type class which simply behave as the underlying type.
For custom types you can add phantom type parameters. This way you can make types distinct while being able to apply the same functions to their values. Say data SpecialString usage = SpecialString String Then you can define a generic function like take :: Int -> SpecialString usage -> SpecialString usage or a specialised function like foo :: SpecialString Foo -> SpecialString Foo

On 11 Oct 2007, at 4:06 pm, Tom Davies basically asked for something equivalent to Ada's type T is new Old_T; which introduces a *distinct* type T that has all the operations and literals of Old_T. In functional terms, suppose there is a function f :: ... Old_T ... Old_T ... Old_T ... then you get a function f :: ... T ... T ... T ... where Old_T has been uniformly and consistently replaced by T. More precisely, this happens to the *built in* operations of the type; it doesn't automatically apply to user-defined operations. It's extraordinarily useful in Ada. My standard example is that you can have a 2D array where row subscripts and column subscripts act in almost all ways like integers BUT they are incompatible types so you cannot possibly mix them up. Haskell's newtype comes close, but not quite close enough. If I write newtype Foo = Foo String I cannot then write x :: Foo x = "boojumed" Similarly, if I write newtype Row = Row Int newtype Col = Col Int I cannot use 1 as a Row or Col value, but must instead write Row 1 or Col 1. This *is* a limitation of the Haskell type system, and it *does* on occasion lead to more long-winded code. But I suspect that it is not a major problem. For the specific case of numeric types, it is possible to get essentially the Ada result, with the same convenience of use, but not the same convenience of setup. newtype Row = Row Int deriving (Eq,Ord,Show) instance Num Row where (Row x) + (Row y) = Row (x + y) (Row x) - (Row y) = Row (x - y) (Row x) * (Row y) = Row (x * y) fromInteger i = Row (fromInteger i) ... instance Integral Row where ... It isn't trivial, but it's easy to write a little script in the language of your choice given the type and constructor name.

On Fri, 2007-10-12 at 11:00 +1300, ok wrote:
On 11 Oct 2007, at 4:06 pm, Tom Davies basically asked for something equivalent to Ada's type T is new Old_T; which introduces a *distinct* type T that has all the operations and literals of Old_T. In functional terms, suppose there is a function f :: ... Old_T ... Old_T ... Old_T ... then you get a function f :: ... T ... T ... T ... where Old_T has been uniformly and consistently replaced by T. More precisely, this happens to the *built in* operations of the type; it doesn't automatically apply to user-defined operations.
It's extraordinarily useful in Ada. My standard example is that you can have a 2D array where row subscripts and column subscripts act in almost all ways like integers BUT they are incompatible types so you cannot possibly mix them up.
Haskell's newtype comes close, but not quite close enough. If I write newtype Foo = Foo String I cannot then write x :: Foo x = "boojumed" Similarly, if I write newtype Row = Row Int newtype Col = Col Int I cannot use 1 as a Row or Col value, but must instead write Row 1 or Col 1.
This *is* a limitation of the Haskell type system, and it *does* on occasion lead to more long-winded code. But I suspect that it is not a major problem.
For the specific case of numeric types, it is possible to get essentially the Ada result, with the same convenience of use, but not the same convenience of setup.
newtype Row = Row Int deriving (Eq,Ord,Show)
instance Num Row where (Row x) + (Row y) = Row (x + y) (Row x) - (Row y) = Row (x - y) (Row x) * (Row y) = Row (x * y) fromInteger i = Row (fromInteger i) ... instance Integral Row where ...
*Warning: GHC extension* newtype Row = Row Int deriving (Eq,Ord,Show,Read,Num,Integral,...) jcc
participants (5)
-
Andrew Wagner
-
Henning Thielemann
-
Jonathan Cast
-
ok
-
Tom Davies