Hello,
I am reading some extant Haskell code that uses Posix signals.... I am confused by the motivation of the following ...
OK .. "type" is really just a synomym and doesn't invoke type checking like "data" type declarations do .. so why don't we have all the "CInts" substituted by "Signal"? I.e. what did I miss? Thanks, Vasili _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
vigalchin:
> Hello, > > I am reading some extant Haskell code that uses Posix signals.... I am > confused by the motivation of the following ... > > type [1]Signal = [2]CInt > [3]nullSignal :: [4]Signal > [5]internalAbort :: [6]Signal > [7]sigABRT :: [8]CInt > [9]realTimeAlarm :: [10]Signal > [11]sigALRM :: [12]CInt > [13]busError :: [14]Signal > [15]sigBUS :: [16]CInt > > OK .. "type" is really just a synomym and doesn't invoke type checking > like "data" type declarations do .. so why don't we have all the "CInts" > substituted by "Signal"? I.e. what did I miss? Looks like it should all be Signal, and probably should be using a newtype, to prevent funky tricks. The Posix layer is a bit crufty. -- Don _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Thanks Don. Maybe both for me and others in order to take the fight to the Klingons and other Baddies, please explain the "typefulness" protection that "newtype" affords over the "Klingon " "type" ... In the code that I contributed to the library, I like to think that I used "newtype" appropriately but not perhaps with full understanding.
Thanks, Vasili On Mon, Oct 6, 2008 at 12:37 AM, Don Stewart <[hidden email]> wrote: vigalchin: _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Used wisely, newtype prevents accidentally constructing illegal values
of Signal type, by treating them as CInts. You can restrict the valid values of the Signal type to be just those signals you define, not arbitrary bit patterns that fit in a CInt. vigalchin: > Thanks Don. Maybe both for me and others in order to take the fight to the > Klingons and other Baddies, please explain the "typefulness" protection > that "newtype" affords over the "Klingon " "type" ... In the code that I > contributed to the library, I like to think that I used "newtype" > appropriately but not perhaps with full understanding. > > Thanks, Vasili > > On Mon, Oct 6, 2008 at 12:37 AM, Don Stewart <[1][hidden email]> wrote: > > vigalchin: > > Hello, > > > > I am reading some extant Haskell code that uses Posix > signals.... I am > > confused by the motivation of the following ... > > > > type [1]Signal = [2]CInt > > [3]nullSignal :: [4]Signal > > [5]internalAbort :: [6]Signal > > [7]sigABRT :: [8]CInt > > [9]realTimeAlarm :: [10]Signal > > [11]sigALRM :: [12]CInt > > [13]busError :: [14]Signal > > [15]sigBUS :: [16]CInt > > > > OK .. "type" is really just a synomym and doesn't invoke type > checking > > like "data" type declarations do .. so why don't we have all the > "CInts" > > substituted by "Signal"? I.e. what did I miss? > > Looks like it should all be Signal, and probably should be using a > newtype, to prevent funky tricks. The Posix layer is a bit crufty. > -- Don > > References > > Visible links > 1. mailto:[hidden email] Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
ok ... by using "newtype", we are constricting/constraining to a subset of CInt .. e.g. something like a "subtype" of CInt?? (where by "subtype", I mean like the notion of subtype in languages like Ada). For our audience, can you perhaps distinguish (in a typeful way) between the Haskell notion of "type", "newtype" and "data"? Or maybe let's distinguish between these notions not only in a typeful manner, but also in a historical motivation? .. ... motivations are always IMO very, very enlightening!
Regards, vasili On Mon, Oct 6, 2008 at 12:47 AM, Don Stewart <[hidden email]> wrote: Used wisely, newtype prevents accidentally constructing illegal values _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
2008/10/6 Galchin, Vasili <[hidden email]>:
> ok ... by using "newtype", we are constricting/constraining to a subset of > CInt .. e.g. something like a "subtype" of CInt?? (where by "subtype", I > mean like the notion of subtype in languages like Ada). For our audience, > can you perhaps distinguish (in a typeful way) between the Haskell notion of > "type", "newtype" and "data"? Or maybe let's distinguish between these > notions not only in a typeful manner, but also in a historical motivation? > .. ... motivations are always IMO very, very enlightening! Here's an example of using newtypes: module Main where newtype Flag = Flag Int -- These are the only legal values: flag1 :: Flag flag1 = Flag 1 flag2 :: Flag flag2 = Flag 2 fun :: Int -> Flag -> Int fun n (Flag f) = undefined -- Implementation goes here. -- Using `fun`. main = do print (fun 10 flag1) -- Oh noes, the programmer messed up and reordered the -- arguments! print (fun flag1 10) Saved by the type checker: /home/tibell/Test.hs:21:13: Couldn't match expected type `Int' against inferred type `Flag' In the first argument of `fun', namely `flag1' In the first argument of `print', namely `(fun flag1 10)' In the expression: print (fun flag1 10) Failed, modules loaded: none. By creating a module that doesn't export the constructor used to create e.g. `Flag` and only the constants `flag1` and `flag2` we can make sure that no one ever calls `fun` with an illegal value. I hope this helps. Cheers, Johan _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Vasili I. Galchin
2008/10/5 Galchin, Vasili <[hidden email]>
If you like historical perspective check out this: http://research.microsoft.com/~simonpj/papers/history-of-haskell/index.htm type is similar to typedef in C. That is, it's more or less just for renaming. I say "more or less" because I'm not sure exactly how the "renaming" works in the presence of rank-2 and higher types. data is essentially for declaring new data structures. As a result of this it also defines a new type. newtype is for reusing existing types or data structures but with a new distinct type. data and newtype vary in one more subtle way, and that's how/when they evaluate to bottom. Most of the time they behave identically, but in the right cases they act sightly differently. newtype is usually regarded as more efficient than data. This is because the compiler can choose to optimize away the newtype so that it only exists at type check time. I think this is also possible with data in some, but not all, uses. Jason _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
dagit:
> data and newtype vary in one more subtle way, and that's how/when they > evaluate to bottom. Most of the time they behave identically, but in the > right cases they act sightly differently. newtype is usually regarded as > more efficient than data. This is because the compiler can choose to > optimize away the newtype so that it only exists at type check time. I > think this is also possible with data in some, but not all, uses. The compiler *must* optimise away the use. They're sort of 'virtual' data, guaranteed to have no runtime cost. -- Don _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
2008/10/6 Don Stewart <[hidden email]>:
> dagit: >> data and newtype vary in one more subtle way, and that's how/when they >> evaluate to bottom. Most of the time they behave identically, but in the >> right cases they act sightly differently. newtype is usually regarded as >> more efficient than data. This is because the compiler can choose to >> optimize away the newtype so that it only exists at type check time. I >> think this is also possible with data in some, but not all, uses. > > The compiler *must* optimise away the use. They're sort of 'virtual' > data, guaranteed to have no runtime cost. I'm not sure that I'd want to be that emphatic about what an implementation *must* do regarding something so operational. The informal semantics of pattern matching in the Report says: Matching the pattern con pat against a value, where con is a constructor defined by newtype, depends on the value: * If the value is of the form con v, then pat is matched against v. * If the value is _|_, then pat is matched against _|_. That is, constructors associated with newtype serve only to change the type of a value. This clearly has an implementation which introduces no overhead, which one can expect from good implementations of the language. There are obviously implementations of these semantics which do introduce overhead as well though, so I would be hesitant to make any requirement like that. We can say however that newtypes have no additional runtime cost in GHC regardless of the optimisation level you pick. - Cale _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
On Mon, Oct 6, 2008 at 14:58, Cale Gibbard <[hidden email]> wrote:
> 2008/10/6 Don Stewart <[hidden email]>: >> dagit: >>> data and newtype vary in one more subtle way, and that's how/when they >>> evaluate to bottom. Most of the time they behave identically, but in the >>> right cases they act sightly differently. newtype is usually regarded as >>> more efficient than data. This is because the compiler can choose to >>> optimize away the newtype so that it only exists at type check time. I >>> think this is also possible with data in some, but not all, uses. >> >> The compiler *must* optimise away the use. They're sort of 'virtual' >> data, guaranteed to have no runtime cost. > > I'm not sure that I'd want to be that emphatic about what an > implementation *must* do regarding something so operational. > > The informal semantics of pattern matching in the Report says: > > Matching the pattern con pat against a value, where con is a > constructor defined by newtype, depends on the value: > * If the value is of the form con v, then pat is matched against v. > * If the value is _|_, then pat is matched against _|_. > That is, constructors associated with newtype serve only to change the > type of a value. > > This clearly has an implementation which introduces no overhead, which > one can expect from good implementations of the language. There are > obviously implementations of these semantics which do introduce > overhead as well though, so I would be hesitant to make any > requirement like that. And this requirement is there why? Is it specifically put in so that one is able to create this overhead-less implementation? Given: data A = A Int newtype B = B Int ta (A x) = True tb (B x) = True This happens (not surprisingly given your above comments): *Main GOA> :load test.hs [1 of 1] Compiling Main ( test.hs, interpreted ) Ok, modules loaded: Main. *Main GOA> ta undefined *** Exception: Prelude.undefined *Main GOA> tb undefined True Why is the x evaluated in ta? cheers, Arnar _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Cale Gibbard
On Mon, Oct 6, 2008 at 2:58 PM, Cale Gibbard <[hidden email]> wrote:
> 2008/10/6 Don Stewart <[hidden email]>: >> dagit: >>> data and newtype vary in one more subtle way, and that's how/when they >>> evaluate to bottom. Most of the time they behave identically, but in the >>> right cases they act sightly differently. newtype is usually regarded as >>> more efficient than data. This is because the compiler can choose to >>> optimize away the newtype so that it only exists at type check time. I >>> think this is also possible with data in some, but not all, uses. >> >> The compiler *must* optimise away the use. They're sort of 'virtual' >> data, guaranteed to have no runtime cost. > > I'm not sure that I'd want to be that emphatic about what an > implementation *must* do regarding something so operational. > > [..] > > We can say however that newtypes have no additional runtime cost in > GHC regardless of the optimisation level you pick. > unnecessary work just for the sake of converting types. Suppose you have a newtype Price = Price Int and you're given [Int] and want to have [Price]. This is simple to do, just 'map Price'. But since Price and Int are represented the same way this ought to be just the identity function. But it is in general very difficult for a compiler to figure out that this traversal of the list in fact is just the identity function. Simple type conversions like these can unfortunately force you to do some work even though the representation is identical. Cheers, Josef _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
In reply to this post by Arnar Birgisson
On Mon, Oct 6, 2008 at 2:19 PM, Arnar Birgisson <[hidden email]> wrote:
> And this requirement is there why? Is it specifically put in so that > one is able to create this overhead-less implementation? > > Given: > > data A = A Int > newtype B = B Int > > ta (A x) = True > tb (B x) = True > > This happens (not surprisingly given your above comments): > > *Main GOA> :load test.hs > [1 of 1] Compiling Main ( test.hs, interpreted ) > Ok, modules loaded: Main. > *Main GOA> ta undefined > *** Exception: Prelude.undefined > *Main GOA> tb undefined > True > > Why is the x evaluated in ta? x isn't evaluated. "undefined" is evaluated to see if it matches the constructor "A". But we don't even get to check, because undefined throws an exception during its evaluation. In the "tb" case, (B x) always matches because B is a newtype. x gets bound to undefined, but never evaluated. -- ryan _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Hi,
On Mon, Oct 6, 2008 at 16:10, Ryan Ingram <[hidden email]> wrote: > On Mon, Oct 6, 2008 at 2:19 PM, Arnar Birgisson <[hidden email]> wrote: >> And this requirement is there why? Is it specifically put in so that >> one is able to create this overhead-less implementation? >> >> Given: >> >> data A = A Int >> newtype B = B Int >> >> ta (A x) = True >> tb (B x) = True >> >> This happens (not surprisingly given your above comments): >> >> *Main GOA> :load test.hs >> [1 of 1] Compiling Main ( test.hs, interpreted ) >> Ok, modules loaded: Main. >> *Main GOA> ta undefined >> *** Exception: Prelude.undefined >> *Main GOA> tb undefined >> True >> >> Why is the x evaluated in ta? > > x isn't evaluated. Yes, realized my error just after hitting send :/ > "undefined" is evaluated to see if it matches the > constructor "A". But we don't even get to check, because undefined > throws an exception during its evaluation. > > In the "tb" case, (B x) always matches because B is a newtype. x gets > bound to undefined, but never evaluated. And this happens because data values are basically pattern matched at run-time but newtype values are matched at compile-time, effectively turning tb into an Int -> Bool function? That explains pretty well why newtype can have only one constructor. cheers, Arnar _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
On Mon, Oct 6, 2008 at 3:30 PM, Arnar Birgisson <[hidden email]> wrote:
>> "undefined" is evaluated to see if it matches the >> constructor "A". But we don't even get to check, because undefined >> throws an exception during its evaluation. >> >> In the "tb" case, (B x) always matches because B is a newtype. x gets >> bound to undefined, but never evaluated. > > And this happens because data values are basically pattern matched at > run-time but newtype values are matched at compile-time, effectively > turning tb into an Int -> Bool function? Yep, that's exactly it. > That explains pretty well why newtype can have only one constructor. I never thought of it that way, but yes, it really does! Also, you can get the same behavior out of "ta" if you write it like this: ta ~(A x) = True The translation looks something like this: f ~(A x) = e => f a = e where x = case a of (A v) -> v -- a,v fresh variables not mentioned in e (or, equivalently) f a = let (A x) = a in e -- "a" some fresh variable not mentioned in e This delays the pattern-matching (and thus, the evaluation of "a") lazily until "x" is demanded, at which point "a" might throw an exception or infinite loop, or, if the type has more than one constructor, fail to pattern match (which also throws an exception). If "x" is never demanded then neither is "a". -- ryan _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
On Mon, Oct 6, 2008 at 18:48, Ryan Ingram <[hidden email]> wrote:
> Also, you can get the same behavior out of "ta" if you write it like this: > > ta ~(A x) = True > > The translation looks something like this: > > f ~(A x) = e > => > f a = e > where x = case a of (A v) -> v -- a,v fresh variables not mentioned in e > > (or, equivalently) > > f a = let (A x) = a in e -- "a" some fresh variable not mentioned in e > > This delays the pattern-matching (and thus, the evaluation of "a") > lazily until "x" is demanded, at which point "a" might throw an > exception or infinite loop, or, if the type has more than one > constructor, fail to pattern match (which also throws an exception). > If "x" is never demanded then neither is "a". Ah, that's pretty neat and subtle. Now, say I have a type created with data that has only one constructor. Couldn't the compiler optimize away this unneccessary evaluation during pattern matching? I.e. it would make what you just wrote implicit in that case. Or perhaps data declarations with just one ctor should really be turned into newtypes by the programmer? cheers, Arnar _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
2008/10/6 Arnar Birgisson <[hidden email]>:
> Ah, that's pretty neat and subtle. Now, say I have a type created with > data that has only one constructor. Couldn't the compiler optimize > away this unneccessary evaluation during pattern matching? I.e. it > would make what you just wrote implicit in that case. Or perhaps data > declarations with just one ctor should really be turned into newtypes > by the programmer? Well, the trouble is that because there are differences in the termination behaviour of programs depending on whether something is a newtype or a data with a single constructor, I think automatic conversion of one to the other is avoided. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
Free forum by Nabble | Edit this page |