Today I made an interesting discovery. We all know the benefits of a strong type system, and often tout it as a major advantage of using Haskell. The discovery I made, was that C programmer don't realise the implications of that, as this comment highlights: http://games.slashdot.org/comments.pl?sid=654821&cid=24716845 Apparently, no one realises that a SEGFAULT is a type error, just not a very helpful one. Bob
On So, 2008-08-23 at 22:16 +0200, Thomas Davie wrote:
Today I made an interesting discovery.
We all know the benefits of a strong type system, and often tout it as a major advantage of using Haskell. The discovery I made, was that C programmer don't realise the implications of that, as this comment highlights:
http://games.slashdot.org/comments.pl?sid=654821&cid=24716845
Apparently, no one realises that a SEGFAULT is a type error, just not a very helpful one.
Bob
Type errors are useful because they emerge at compile time and prevent you from compiling (and running) a broken program. A segfault is a runtime error and as such provides no such guide -- it may or may not arise and you don't know something's wrong until sigsegv kills your app, screws all your data, crashes the airplane etc. (without the possibility to tell whether/when it will happen). Matus
On 23 Aug 2008, at 22:36, Matus Tejiscak wrote:
On So, 2008-08-23 at 22:16 +0200, Thomas Davie wrote:
Today I made an interesting discovery.
We all know the benefits of a strong type system, and often tout it as a major advantage of using Haskell. The discovery I made, was that C programmer don't realise the implications of that, as this comment highlights:
http://games.slashdot.org/comments.pl?sid=654821&cid=24716845
Apparently, no one realises that a SEGFAULT is a type error, just not a very helpful one.
Bob
Type errors are useful because they emerge at compile time and prevent you from compiling (and running) a broken program. A segfault is a runtime error and as such provides no such guide -- it may or may not arise and you don't know something's wrong until sigsegv kills your app, screws all your data, crashes the airplane etc. (without the possibility to tell whether/when it will happen).
I guess I didn't express my point very clearly... That C programmers apparently don't realise that a type system that's sound will give them something -- i.e. their programmer won't ever segfault. I wonder when we try to advertise Haskell if we should be saying "we can give you programs that never segfault", instead of "we have a strong type system". Bob
I guess I didn't express my point very clearly... That C programmers apparently don't realise that a type system that's sound will give them something -- i.e. their programmer won't ever segfault. I wonder when we try to advertise Haskell if we should be saying "we can give you programs that never segfault", instead of "we have a strong type system".
That would be overpromissing. You can definitely get segfaults in Haskell. The obvious example being http://codepad.org/Q8cgS6x8 but many less contrived and more unexpected examples arise naturally (unfortunately). By the way, the Java camp has (correctly) been touting this argument for quite a while.
Bob
Tim Newsham http://www.thenewsh.com/~newsham/
newsham:
I guess I didn't express my point very clearly... That C programmers apparently don't realise that a type system that's sound will give them something -- i.e. their programmer won't ever segfault. I wonder when we try to advertise Haskell if we should be saying "we can give you programs that never segfault", instead of "we have a strong type system".
That would be overpromissing. You can definitely get segfaults in Haskell. The obvious example being
but many less contrived and more unexpected examples arise naturally (unfortunately).
By the way, the Java camp has (correctly) been touting this argument for quite a while.
And it is just a property of strong typing, not static typing. The Pythonists would happily make the same remark. We promise both safety and efficiency. -- Don
On 23 Aug 2008, at 23:10, Tim Newsham wrote:
I guess I didn't express my point very clearly... That C programmers apparently don't realise that a type system that's sound will give them something -- i.e. their programmer won't ever segfault. I wonder when we try to advertise Haskell if we should be saying "we can give you programs that never segfault", instead of "we have a strong type system".
That would be overpromissing. You can definitely get segfaults in Haskell. The obvious example being
but many less contrived and more unexpected examples arise naturally (unfortunately).
By the way, the Java camp has (correctly) been touting this argument for quite a while.
I'd be interested to see your other examples -- because that error is not happening in Haskell! You can't argue that Haskell doesn't give you no segfaults, because you can embed a C segfault within Haskell. Bob
Am Samstag, 23. August 2008 23:17 schrieb Thomas Davie:
I'd be interested to see your other examples -- because that error is not happening in Haskell! You can't argue that Haskell doesn't give you no segfaults, because you can embed a C segfault within Haskell.
Bob
Use ST(U)Arrays, and use unsafeWrite because you do the indexchecking yourself. Then be stupid and confuse two bounds so that you actually write beyond the array bounds. I've had that happen _once_. But if you explicitly say you want it unsafe, you're prepared for it :) Daniel
You can get nice exception (but not segfault) when trying to fire this code:
{-# OPTIONS_GHC -XRecursiveDo #-}
module Main where
import Control.Concurrent import Control.Monad.Fix
loopChan :: IO (Chan ()) loopChan = mdo chan <- dupChan chan return chan
main = do c <- loopChan writeChan c ()
You just can't use a duplicate of a channel to create itself.
Prelude> :l loop_channel.hs [1 of 1] Compiling LC ( loop_channel.hs, interpreted ) Ok, modules loaded: LC. *LC> :type loopChan loopChan :: IO (Chan ()) *LC> c <- loopChan *** Exception: <>
Still I don't think it's a bug, but merely complicated way to crash your program. Haskell is great, but it also has many pitfalls, just like any other language. Christopher Skrzętnicki On Sun, Aug 24, 2008 at 00:15, Daniel Fischer <daniel.is.fischer@web.de> wrote:
Am Samstag, 23. August 2008 23:17 schrieb Thomas Davie:
I'd be interested to see your other examples -- because that error is not happening in Haskell! You can't argue that Haskell doesn't give you no segfaults, because you can embed a C segfault within Haskell.
Bob
Use ST(U)Arrays, and use unsafeWrite because you do the indexchecking yourself. Then be stupid and confuse two bounds so that you actually write beyond the array bounds. I've had that happen _once_. But if you explicitly say you want it unsafe, you're prepared for it :)
Daniel _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
On Sat, Aug 23, 2008 at 6:15 PM, Daniel Fischer <daniel.is.fischer@web.de> wrote:
Am Samstag, 23. August 2008 23:17 schrieb Thomas Davie:
I'd be interested to see your other examples -- because that error is not happening in Haskell! You can't argue that Haskell doesn't give you no segfaults, because you can embed a C segfault within Haskell.
Use ST(U)Arrays, and use unsafeWrite because you do the indexchecking yourself. Then be stupid and confuse two bounds so that you actually write beyond the array bounds. I've had that happen _once_. But if you explicitly say you want it unsafe, you're prepared for it :)
Which illustrates the point that it's not type safety that protects us from segfaults, so much as bounds checking, and that's got a non-trivial runtime cost. At least, most segfaults that *I've* caused (in C or C++) have been from overwriting the bounds of arrays, and that's precisely the problem that Haskell does *not* solve using its type system. There have attempts to do so, but I've not heard of instances where they have been used in real programs. David
David Roundy wrote:
Which illustrates the point that it's not type safety that protects us from segfaults, so much as bounds checking, and that's got a non-trivial runtime cost. At least, most segfaults that *I've* caused (in C or C++) have been from overwriting the bounds of arrays, and that's precisely the problem that Haskell does *not* solve using its type system.
That differs from my experience. Most segfaults that *I've* caused (in C or C++) have been due to dereferencing null pointers. Type safety does help you here, in that Maybe lets you distinguish the types of things that are optionally present from those that must be. Tim
Tim Docker wrote:
David Roundy wrote:
Which illustrates the point that it's not type safety that protects us from segfaults, so much as bounds checking, and that's got a non-trivial runtime cost. At least, most segfaults that *I've* caused (in C or C++) have been from overwriting the bounds of arrays, and that's precisely the problem that Haskell does *not* solve using its type system.
That differs from my experience. Most segfaults that *I've* caused (in C or C++) have been due to dereferencing null pointers. Type safety does help you here, in that Maybe lets you distinguish the types of things that are optionally present from those that must be.
Tim
Huh? Type safety buys you not having to worry about dereferencing stale nonnull pointers (lifetime of reference exceeding lifetime of referent), but nothing about dereferencing null pointers, which are the moral equivalent of Nothing. Failure to handle a null pointer is just like using fromJust and results in the same program termination (undefined). Dan
On Wed, 2008-08-27 at 12:23 -0700, Dan Weston wrote:
Tim Docker wrote:
David Roundy wrote:
Which illustrates the point that it's not type safety that protects us from segfaults, so much as bounds checking, and that's got a non-trivial runtime cost. At least, most segfaults that *I've* caused (in C or C++) have been from overwriting the bounds of arrays, and that's precisely the problem that Haskell does *not* solve using its type system.
That differs from my experience. Most segfaults that *I've* caused (in C or C++) have been due to dereferencing null pointers. Type safety does help you here, in that Maybe lets you distinguish the types of things that are optionally present from those that must be.
Tim
Huh? Type safety buys you not having to worry about dereferencing stale nonnull pointers (lifetime of reference exceeding lifetime of referent), but nothing about dereferencing null pointers, which are the moral equivalent of Nothing.
Actually, that's garbage collection.
Failure to handle a null pointer is just like using fromJust and results in the same program termination (undefined).
jcc
On Aug 27, 2008, at 12:23 PM, Dan Weston wrote:
Huh? Type safety buys you not having to worry about dereferencing stale nonnull pointers (lifetime of reference exceeding lifetime of referent), but nothing about dereferencing null pointers, which are the moral equivalent of Nothing.
What type safety buys you, in my mind, is that Nothing is only a valid value for explicit Maybe types. In cases where you don't use Maybe, the "null" situation just can't occur. In languages with null pointers, any pointer could possibly be null. When you do use Maybe, you have to explicitly handle the Just and Nothing cases separately. Pattern matching reminds you that both are possible. I tend to view fromJust as a function you should only use if you're _very_, _very_ sure that the Nothing case is impossible. But, if so, why are you using a Maybe type in the first place? Aaron
On Wed, 2008-08-27 at 13:34 -0700, Aaron Tomb wrote:
On Aug 27, 2008, at 12:23 PM, Dan Weston wrote:
Huh? Type safety buys you not having to worry about dereferencing stale nonnull pointers (lifetime of reference exceeding lifetime of referent), but nothing about dereferencing null pointers, which are the moral equivalent of Nothing.
What type safety buys you, in my mind, is that Nothing is only a valid value for explicit Maybe types. In cases where you don't use Maybe, the "null" situation just can't occur. In languages with null pointers, any pointer could possibly be null.
When you do use Maybe, you have to explicitly handle the Just and Nothing cases separately. Pattern matching reminds you that both are possible. I tend to view fromJust as a function you should only use if you're _very_, _very_ sure that the Nothing case is impossible. But, if so, why are you using a Maybe type in the first place?
Consider: substType v ty x = fromJust $ do TyVar s <- cast x guard $ v == s cast ty `mplus` do ForAll s ty' <- cast x guard $ v == s cast $ ForAll s ty' `mplus` do ForAll s ty' <- cast x guard $ s `Set.member` freeTV ty let v' = Set.findMax (freeTV ty `Set.union` freeTV ty') ++ "1" ty'' = substType v ty $ substType s (TyVar v') $ ty' cast $ ForAll v' ty'' `mplus` do ForAll s ty' <- cast x cast $ ForAll s $ substType v ty ty' `mplus` do TyLambda s ty' <- cast x guard $ v == s cast $ TyLambda s ty' `mplus` do TyLambda s ty' <- cast x guard $ s `Set.member` freeTV ty let v' = Set.findMax (freeTV ty `Set.union` freeTV ty') ++ "1" ty'' = substType v ty $ substType s (TyVar v') $ ty' cast $ TyLambda v' ty'' `mplus` do TyLambda s ty' <- cast x cast $ TyLambda s $ substType v ty ty' `mplus` do return $ gmapT (substType v ty) x ForAll :: String -> Type -> Type TyLambda :: String -> Expr -> Expr The last `case' is a catch-all, so you do know the result of the mplus's is a Just, but you still need the Maybe. jcc
On 2008 Aug 27, at 16:39, Jonathan Cast wrote:
The last `case' is a catch-all, so you do know the result of the mplus's is a Just, but you still need the Maybe.
I have to admit my thought here is that the problem isn't the Maybe, it's the fromJust. Make it go away, force people to explicitly pattern match so they have to think about it (and hopefully the compiler warns them of the missing pattern). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
Am Mittwoch, 27. August 2008 22:34 schrieb Aaron Tomb:
On Aug 27, 2008, at 12:23 PM, Dan Weston wrote:
Huh? Type safety buys you not having to worry about dereferencing stale nonnull pointers (lifetime of reference exceeding lifetime of referent), but nothing about dereferencing null pointers, which are the moral equivalent of Nothing.
What type safety buys you, in my mind, is that Nothing is only a valid value for explicit Maybe types. In cases where you don't use Maybe, the "null" situation just can't occur. In languages with null pointers, any pointer could possibly be null.
When you do use Maybe, you have to explicitly handle the Just and Nothing cases separately. Pattern matching reminds you that both are possible. I tend to view fromJust as a function you should only use if you're _very_, _very_ sure that the Nothing case is impossible. But, if so, why are you using a Maybe type in the first place?
Good question. Maybe because you use a function which returns a Maybe result but you know in your use case it's always a Just. But then still a case fun args of Just val -> use val Nothing -> error $ LOCATION ++ " Hey, that ought to be impossible! Go investigate!" would be cleaner. If fromJust is more efficient and this code is called many times in your programme, that could count as a reason.
Aaron
Cheers, Daniel
On 2008 Aug 27, at 16:49, Daniel Fischer wrote:
Am Mittwoch, 27. August 2008 22:34 schrieb Aaron Tomb:
When you do use Maybe, you have to explicitly handle the Just and Nothing cases separately. Pattern matching reminds you that both are possible. I tend to view fromJust as a function you should only use if you're _very_, _very_ sure that the Nothing case is impossible. But, if so, why are you using a Maybe type in the first place?
Good question. Maybe because you use a function which returns a Maybe result but you know in your use case it's always a Just. But then still a case fun args of Just val -> use val Nothing -> error $ LOCATION ++ " Hey, that ought to be impossible! Go investigate!"
would be cleaner. If fromJust is more efficient and this code is called many times in your programme, that could count as a reason.
I have more than once seen a NULL dereference not get caught until later (admittedly I think most POSIX-like systems unmap page 0 these days; but think of embedded systems or other non-POSIX environments). even fromJust gives you more of an ability to track the problem down in that case. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
Brandon S. Allbery KF8NH wrote:
On 2008 Aug 27, at 16:49, Daniel Fischer wrote:
Am Mittwoch, 27. August 2008 22:34 schrieb Aaron Tomb:
When you do use Maybe, you have to explicitly handle the Just and Nothing cases separately. Pattern matching reminds you that both are possible. I tend to view fromJust as a function you should only use if you're _very_, _very_ sure that the Nothing case is impossible. But, if so, why are you using a Maybe type in the first place?
Good question. Maybe because you use a function which returns a Maybe result but you know in your use case it's always a Just. But then still a case fun args of Just val -> use val Nothing -> error $ LOCATION ++ " Hey, that ought to be impossible! Go investigate!"
would be cleaner. If fromJust is more efficient and this code is called many times in your programme, that could count as a reason.
I have more than once seen a NULL dereference not get caught until later (admittedly I think most POSIX-like systems unmap page 0 these days; but think of embedded systems or other non-POSIX environments).
Oh yes, embedded systems indeed. We have only recently discovered a subtle bug in some C code I wrote years ago where I mixed up the order of two statements. The result was that memory at address zero was overwritten with a string. Interestingly, we /never/ had any segfaults, exceptions, whatever, not even broken behavior of any sort. I discovered the problem only when I explicitly looked at the memory area in question to find out what the system uses as default interrupt handler (yes, this was VxWorks on an 68040, and the interrupt vector table is located at zero). In the end I managed to crash a computer by issuing '1/0' on the shell (apparently the vector for the division by zero fault handler had been overwritten)... however, that was after I already knew there was something bad going on. Note that C compilers are, in principle, free to interpret the value 0, when compared or assigned to a pointer, as any address they like, so in principle the C compiler could 're-map' 0 to some never-mapped memory region. I know of no case where this is done, though.
even fromJust gives you more of an ability to track the problem down in that case.
Definitely. Cheers Ben
Aaron Tomb <atomb@galois.com> writes:
Huh? Type safety buys [...] nothing about dereferencing null pointers, which are the moral equivalent of Nothing.
What type safety buys you, in my mind, is that Nothing is only a valid value for explicit Maybe types. In cases where you don't use Maybe, the "null" situation just can't occur. In languages with null pointers, any pointer could possibly be null.
To write Haskell that is obviously safe, you need to check all cases of algebraic data types - both Just and Nothing. To do something similar in C, you need to check every pointer for NULL. The great thing about Maybe is that once I've checked it isn't Nothing, I can extract the value and dispense with further checks. foo mbx = maybe default (bar x) mbx Usually, you don't want to go on processing a NULL pointer anyway, but in C, 'bar' might be called with NULL, so it'll have to check it again. And 'bar' is less likely than 'foo' to be able to do anything sensible with NULL, and thus 'foo' needs to be able to handle errors returned from 'bar', too. I think an important reason we get NULL pointer SEGVs is that all this checking when you "know" it can't happen gets tedious, and thus ignored and forgotten. -k -- If I haven't seen further, it is by standing in the footprints of giants
ketil:
Aaron Tomb <atomb@galois.com> writes:
Huh? Type safety buys [...] nothing about dereferencing null pointers, which are the moral equivalent of Nothing.
What type safety buys you, in my mind, is that Nothing is only a valid value for explicit Maybe types. In cases where you don't use Maybe, the "null" situation just can't occur. In languages with null pointers, any pointer could possibly be null.
To write Haskell that is obviously safe, you need to check all cases of algebraic data types - both Just and Nothing. To do something similar in C, you need to check every pointer for NULL.
The great thing about Maybe is that once I've checked it isn't Nothing, I can extract the value and dispense with further checks.
foo mbx = maybe default (bar x) mbx
And GHC will warn me when I forget to check all cases, and prevent me from compiling at all, if I don't do any check. -- Don
On 2008 Aug 28, at 2:41, Don Stewart wrote:
ketil:
The great thing about Maybe is that once I've checked it isn't Nothing, I can extract the value and dispense with further checks.
foo mbx = maybe default (bar x) mbx
And GHC will warn me when I forget to check all cases, and prevent me from compiling at all, if I don't do any check.
...unless you use fromJust. GNU ld supports "pragmas" which cause the use of certain functions to output warnings at link time (try compiling a C program that uses gets()). It occurs to me that this, either in compiler or linker, would be a nice thing for ghc to do when using fromJust or other partial functions. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
GNU ld supports "pragmas" which cause the use of certain functions to output warnings at link time (try compiling a C program that uses gets()). It occurs to me that this, either in compiler or linker, would be a nice thing for ghc to do when using fromJust or other partial functions.
would you include all partial functions in this, such as head?
brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com
Tim Newsham http://www.thenewsh.com/~newsham/
On 2008 Aug 28, at 13:21, Tim Newsham wrote:
GNU ld supports "pragmas" which cause the use of certain functions to output warnings at link time (try compiling a C program that uses gets()). It occurs to me that this, either in compiler or linker, would be a nice thing for ghc to do when using fromJust or other partial functions.
would you include all partial functions in this, such as head?
I'd like to, but IMO head is a little more acceptable because lists aren't binary like Maybe. fromJust really is the Haskell equivalent of dereferencing a pointer without checking if it's NULL, aside from being more reliably detectable. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
On Thu, 28 Aug 2008, Brandon S. Allbery KF8NH wrote:
On 2008 Aug 28, at 13:21, Tim Newsham wrote:
GNU ld supports "pragmas" which cause the use of certain functions to output warnings at link time (try compiling a C program that uses gets()). It occurs to me that this, either in compiler or linker, would be a nice thing for ghc to do when using fromJust or other partial functions.
would you include all partial functions in this, such as head?
I'd like to, but IMO head is a little more acceptable because lists aren't binary like Maybe. fromJust really is the Haskell equivalent of dereferencing a pointer without checking if it's NULL, aside from being more reliably detectable.
-- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
Tools like Neil Mitchell's Catch can do more sophisticated checking, as long as your program can be compiled by YHC. Sometimes fromJust can be quite useful, though, especially in tandem with isJust. For example,
prop_foobar :: SomeType -> Property prop_foobar x = isJust (someTypeToMaybe x) ==> fromJust x == expectedResult
Alex
Hi
Tools like Neil Mitchell's Catch can do more sophisticated checking, as long as your program can be compiled by YHC. Sometimes fromJust can be quite useful, though, especially in tandem with isJust. For example,
prop_foobar :: SomeType -> Property prop_foobar x = isJust (someTypeToMaybe x) ==> fromJust x == expectedResult
I was thinking of jumping in on this thread, to advertise Catch, but unfortunately "can be compiled with Yhc" makes it a fairly niche tool :-( However, the fundamental bit of Catch works on an a normal Core language, and I do want to hook it up to GHC's Core language at some point. It could certainly deal with prop_foobar, and things that are far more complex. To get an idea of what Catch can do, I recommend reading the draft paper and skipping to section 5.4 where I verify the entire HsColour program with one small refactoring and no annotations. I even found 3 real crashing bugs in HsColour, that were fixed because Catch pointed them out. Incomplete patterns + a checker (Catch) are just a faster and more efficient way of writing complete patterns without dead code :-) Catch: http://www-users.cs.york.ac.uk/~ndm/catch/ Thanks Neil
On Thu, Aug 28, 2008 at 5:02 PM, Neil Mitchell <ndmitchell@gmail.com> wrote:
Hi
Tools like Neil Mitchell's Catch can do more sophisticated checking, as long as your program can be compiled by YHC. Sometimes fromJust can be quite useful, though, especially in tandem with isJust. For example,
prop_foobar :: SomeType -> Property prop_foobar x = isJust (someTypeToMaybe x) ==> fromJust x == expectedResult
I was thinking of jumping in on this thread, to advertise Catch, but unfortunately "can be compiled with Yhc" makes it a fairly niche tool :-( However, the fundamental bit of Catch works on an a normal Core language, and I do want to hook it up to GHC's Core language at some point. It could certainly deal with prop_foobar, and things that are far more complex.
So are we going to get ghc -Wcatch? That would be really cool. catch has always sounded very interesting, but I've never used it because I use too many ghc libs.
--- On Wed, 8/27/08, Dan Weston <westondan@imageworks.com> wrote:
Failure to handle a null pointer is just like using fromJust and results in the same program termination (undefined).
Dan
Well, not (IMHO) 'just like': 'fromJust Nothing' turns into a 'catchable' exception in the IO Monad, but a SEGFAULT certainly doesn't. E.g.
import Control.Exception as C import Data.Maybe main = do (fromJust Nothing >>= ( \ s -> putStrLn s)) `C.catch` (\ _ -> putStrLn "ok")
prints 'ok', whereas:
import Foreign.Ptr import Foreign.Storable import qualified Control.Exception as E main = poke nullPtr '\0' `E.catch` (\ _ -> putStrLn "ok")
just segfaults...
Dan Weston wrote:
Tim Docker wrote:
That differs from my experience. Most segfaults that *I've* caused (in C or C++) have been due to dereferencing null pointers. Type safety does help you here, in that Maybe lets you distinguish the types of things that are optionally present from those that must be.
Huh? Type safety buys you not having to worry about dereferencing stale nonnull pointers (lifetime of reference exceeding lifetime of referent), but nothing about dereferencing null pointers, which are the moral equivalent of Nothing.
Failure to handle a null pointer is just like using fromJust and results in the same program termination (undefined).
Well as someone else pointed out, you can reliably catch a pattern match failure. You may or may not be able to catch a segfault. But my point is that haskell trivially lets you distinguish between values (type X), and nullable values (type Maybe X), and doing so is standard practice when using the language. The compiler will disallow any inconsistency in the use of these two types. C however, does not have a compile time distinction between a pointer to a value that might be null, and one that is guaranteed not to be null. The code writer must track this distinction manually. Mistakes result in segvs, not compile errors. Tim
Thomas Davie wrote:
I'd be interested to see your other examples -- because that error is not happening in Haskell! You can't argue that Haskell doesn't give you no segfaults, because you can embed a C segfault within Haskell.
This segfaults on my x86_64 Linux box: module Main where import Data.Typeable import Data.IORef data T = T instance Typeable T where typeOf _ = typeOf (undefined :: IORef ()) main :: IO () main = writeIORef (maybe undefined id (cast T)) () You'll note nothing marked "Foreign" or "unsafe", and only the base library used. Does the segfault "happen in Haskell" or not? -- Ashley Yakeley
ashley:
Thomas Davie wrote:
I'd be interested to see your other examples -- because that error is not happening in Haskell! You can't argue that Haskell doesn't give you no segfaults, because you can embed a C segfault within Haskell.
This segfaults on my x86_64 Linux box:
module Main where import Data.Typeable import Data.IORef data T = T instance Typeable T where typeOf _ = typeOf (undefined :: IORef ()) main :: IO () main = writeIORef (maybe undefined id (cast T)) ()
You'll note nothing marked "Foreign" or "unsafe", and only the base library used. Does the segfault "happen in Haskell" or not?
You just wrote unsafeCoere# a different way: typeOf T = typeOf (undefined :: IORef ()) Manual Typeable deriving should probably be disabled :-) -- Don
Don Stewart wrote:
You just wrote unsafeCoere# a different way:
typeOf T = typeOf (undefined :: IORef ())
Right. It's straightforward to write unsafe segfaulting code in apparently safe Haskell.
Manual Typeable deriving should probably be disabled :-)
Another ugly special-case hack. There's a better way. -- Ashley Yakeley
On 2008 Aug 25, at 0:33, Ashley Yakeley wrote:
Don Stewart wrote:
You just wrote unsafeCoere# a different way: typeOf T = typeOf (undefined :: IORef ())
Right. It's straightforward to write unsafe segfaulting code in apparently safe Haskell.
typeOf / Typeable is itself an ugly special case, and really should be designed into the language: it's about runtime type information. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
Hello,
Manual Typeable deriving should probably be disabled :-)
There are legitimate reasons to define your own Typeable instances. Since Typeable already contains all the machinery you need to type a standard functional language, it is nice to just add Typeable instances when defining your own DSL which builds upon standard language constructs; the alternative is to recreate the Typeable machinery for your own type representations. -Jeff
Don Stewart wrote:
ashley:
Thomas Davie wrote:
I'd be interested to see your other examples -- because that error is not happening in Haskell! You can't argue that Haskell doesn't give you no segfaults, because you can embed a C segfault within Haskell.
This segfaults on my x86_64 Linux box:
module Main where import Data.Typeable import Data.IORef data T = T instance Typeable T where typeOf _ = typeOf (undefined :: IORef ()) main :: IO () main = writeIORef (maybe undefined id (cast T)) ()
You'll note nothing marked "Foreign" or "unsafe", and only the base library used. Does the segfault "happen in Haskell" or not?
You just wrote unsafeCoere# a different way:
typeOf T = typeOf (undefined :: IORef ())
Manual Typeable deriving should probably be disabled :-)
But then we need stand-alone deriving clause, otherwise you are lost if someone didn't derive Typeable in her library. We need stand-alone deriving anyway. Cheers Ben
On 23 Aug 2008, at 23:10, Tim Newsham wrote:
I guess I didn't express my point very clearly... That C programmers apparently don't realise that a type system that's sound will give them something -- i.e. their programmer won't ever segfault. I wonder when we try to advertise Haskell if we should be saying "we can give you programs that never segfault", instead of "we have a strong type system".
By the way, the Java camp has (correctly) been touting this argument for quite a while.
As a day-time java programmer, I can say from experience that sometimes (100% pure) Java programs DO segfault. I've had it happen to me, and while you can justifiably say it's an error in the JVM somehow triggered by your program behaviour/timing, that doesn't help you very much at the time. Maarten Hazewinkel
As a day-time java programmer, I can say from experience that sometimes (100% pure) Java programs DO segfault.
I've had it happen to me, and while you can justifiably say it's an error in the JVM somehow triggered by your program behaviour/timing, that doesn't help you very much at the time.
Sure, and just as java has its runtime, so does Haskell. Runtimes are software and prone to being buggy. Runtimes are often written in languages like C where bugs can lead to memory corruption, crashes and arbitrary malicious code execution. Here's a small example that I unfortunately ran across today: ghc -c wxdirect/src/CompileClassTypes.hs -o dist/wxdirect/CompileClassTypes.o -idist/wxdirect -odir dist/wxdirect -hidir dist/wxdirect -package parsec -cpp -package time Segmentation fault (core dumped) :(
Maarten Hazewinkel
The point being that Haskell, while having a theorem checker that helps programmers find and avoid bugs and while being based on semi-formal concepts that can be used to avoid some pitfalls, is still no silver bullet against any and all crashes. Promising would-be converts that it is will only lead to disappointment. Tim Newsham http://www.thenewsh.com/~newsham/
I guess I didn't express my point very clearly... That C programmers apparently don't realise that a type system that's sound will give them something -- i.e. their programmer won't ever segfault. I wonder when we try to advertise Haskell if we should be saying "we can give you programs that never segfault", instead of "we have a strong type system".
I'm sure this point is already made somewhere. But I certainly agree that it's a useful point to make to C programmers who are interested in Haskell. I wonder whether seg faults are the true analogue to errors such as "error: head empty list." or pattern match errors. Not that I'm saying we should be encouraging such errors in our Haskell programs. Chris.
On 2008 Aug 23, at 17:29, C.M.Brown wrote:
I wonder whether seg faults are the true analogue to errors such as "error: head empty list." or pattern match errors.
Not really; while laziness does introduce a certain amount of "spooky action at a difference" to such errors, it's not nearly as bad as the memory corruption (due to effective type mismatches) that often leads to the segfault. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
Hi Brandon, OK, so you're basically saying that segfaults can be eliminated with a strong type system, whereas pattern matching errors is the result of some dodgy laziness going on? I personally think such pattern matching errors are a weaknesss of the language; with possibly no solutions to resolve. regards, Chris. On Sat, 23 Aug 2008, Brandon S. Allbery KF8NH wrote:
On 2008 Aug 23, at 17:29, C.M.Brown wrote:
I wonder whether seg faults are the true analogue to errors such as "error: head empty list." or pattern match errors.
Not really; while laziness does introduce a certain amount of "spooky action at a difference" to such errors, it's not nearly as bad as the memory corruption (due to effective type mismatches) that often leads to the segfault.
On 2008 Aug 24, at 7:16, C.M.Brown wrote:
OK, so you're basically saying that segfaults can be eliminated with a strong type system, whereas pattern matching errors is the result of some
Not really, no. A sufficiently strong type system will eliminate segfaults (modulo bugs in the language runtime or erroneous use of the FFI); but Haskell arguably doesn't have a sufficiently strong type system: pattern match errors being the symptom, and the resolution is dependent types. That said, what I was really saying was that a pattern match error still gives you more information, and more easily, than a segfault: if it's not something trivial like dereferencing a null pointer, the segfault may well be in completely unrelated code because the erroneous operation (for example) overran a buffer and corrupted unrelated data. Whereas with laziness, you may have to do some footwork to find the actual error (ghci's debugger helps, as does hpc as someone else mentioned upthread) but it's deterministic. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
Chris said:
I personally think such pattern matching errors are a weaknesss of the language; with possibly no solutions to resolve.
Actually tools like CATCH [1] exist and could be incorporated into a compiler to eliminate this problem. [1] http://www-users.cs.york.ac.uk/~ndm/catch/
Thomas Davie wrote: > http://games.slashdot.org/comments.pl?sid=654821&cid=24716845 > > Apparently, no one realises that a SEGFAULT is a type error, just not > a very helpful one. Right, so here's an action plan for a clueless C-programmer: - Recompile program in debug mode. - Start application in debugger. - See where error happens, check out call-stack, check out local variables and possibly some heap. - Fix problem, repeat action plan if needed. Here's an error the Haskell run-time system might throw: *** Exception: Prelude.head: empty list (or whatever) So now what? Action plan = []. Perhaps Hat could help here? Or the new GHCi debugger? As a professional C++ programmer and a free-time fan of Haskell I'm not sure e.g. Visual Studio and the Haskell debugging tools I've just mentioned are on the same level of usability. Be careful with your propaganda before you have the real-world tools to back it up. Regards, Niels, devil's advocate, never idealistic.
On Sat, Aug 23, 2008 at 6:29 PM, Niels Aan de Brugh <nielsadb@gmail.com> wrote:
Here's an error the Haskell run-time system might throw: *** Exception: Prelude.head: empty list (or whatever)
So now what? Action plan = [].
brian wrote:
Here's an error the Haskell run-time system might throw: *** Exception: Prelude.head: empty list (or whatever)
So now what? Action plan = [].
Thank you for the URL, but I'm aware of the work in GHC(i). As I've mentioned in my previous e-mail the tooling is definitely not on-par with something like Visual Studio, or perhaps Eclipse for Java. And by not on-par I mean it's miles away from the same level. Microsoft engineers have surely done a good job in the Visual Studio 2005 debugger and unfortunately Microsoft Research doesn't have the same resources to work on the Haskell debugger. Referring to the OP: if Haskell is ever to become a mainstream language (and I certainly hope it will!) an IDE and integrated visual debugger is a must. Until then, perhaps is better to be conservative when kicking on the C language, it has survived worse storms (and not because it's the better language). Regards, Niels
wrt head [], Niels said:
So now what? Action plan = []
Oh come now. Between ghci, hpc, and manual analysis I've never hit a Haskell error and thrown my hands up, "I can't go any further, I'm at a complete loss!" Also it helps that I run into this extremely rarely - I have a larger habit of hidding black holes :-( Niels said:
Thank you for the URL, but I'm aware of the work in GHC(i).
It might interest you to know some people actually use hpc for debugging with reasonble success - it colors the unevaluated sections and this can be very helpful in determining where a program stopped and threw an exception. If you have your eyes on the future you should see Dana Xu's work on static contract checking (check out her Cambridge page). Cheers, Tom
On 2008.08.24 01:29:18 +0200, Niels Aan de Brugh <nielsadb@gmail.com> scribbled 1.0K characters: > Thomas Davie wrote: >> http://games.slashdot.org/comments.pl?sid=654821&cid=24716845 >> >> Apparently, no one realises that a SEGFAULT is a type error, just not >> a very helpful one. > Right, so here's an action plan for a clueless C-programmer: > - Recompile program in debug mode. > - Start application in debugger. > - See where error happens, check out call-stack, check out local > variables and possibly some heap. > - Fix problem, repeat action plan if needed. > > Here's an error the Haskell run-time system might throw: > *** Exception: Prelude.head: empty list > (or whatever) > > So now what? Action plan = []. Perhaps Hat could help here? Or the new > GHCi debugger? As a professional C++ programmer and a free-time fan of > Haskell I'm not sure e.g. Visual Studio and the Haskell debugging tools > I've just mentioned are on the same level of usability. > > Be careful with your propaganda before you have the real-world tools to > back it up. > > Regards, > Niels, devil's advocate, never idealistic. What do I do? In the very rare case that it isn't immediately obvious where the head is coming from, I pull out the interlude library: <http://hackage.haskell.org/cgi-bin/hackage-scripts/package/interlude>. 2 or 3 lines later, I run the binary and find out the answer. -- gwern CESID Security Delta d DUVDEVAN SRI composition data-haven SONANGOL World
"Dear friends, Haskell prevents more errors and earlier." This is honest, relevant, good advocacy. "Dear friends, segfaults are type errors, not logical errors." Why would you indulge in this? It's even less relevant than bikeshed colours. On the bright side, only mainstream communities have enough people with enough time for such word-plays; this indicates that Haskell is mainstream.
On 24 Aug 2008, at 05:04, Albert Y. C. Lai wrote:
"Dear friends, Haskell prevents more errors and earlier." This is honest, relevant, good advocacy.
"Dear friends, segfaults are type errors, not logical errors." Why would you indulge in this? It's even less relevant than bikeshed colours.
Is it? when I write C I spend a lot of my time sat in gdb trying to figure out where the error that the Haskell type system would have caught for me is. This is *very* relevant, it's right at the bottom line of whether I'm more productive in Haskell or in C. Bob
On 2008 Aug 24, at 4:00, Thomas Davie wrote:
On 24 Aug 2008, at 05:04, Albert Y. C. Lai wrote:
"Dear friends, Haskell prevents more errors and earlier." This is honest, relevant, good advocacy.
"Dear friends, segfaults are type errors, not logical errors." Why would you indulge in this? It's even less relevant than bikeshed colours.
Is it? when I write C I spend a lot of my time sat in gdb trying to figure out where the error that the Haskell type system would have caught for me is. This is *very* relevant, it's right at the bottom line of whether I'm more productive in Haskell or in C.
Half agreed. It occurs to me this morning that one thing to consider is that often segfaults are more the kind of error that requires dependent types, which are Hard in Haskell (see, for example, the discussion of type-level naturals elsethread). (Example: copying a buffer when the copy length is negative due to a bug.) -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
participants (28)
-
Aaron Tomb -
ajb@spamcop.net -
Albert Y. C. Lai -
Alexander Dunlap -
Ashley Yakeley -
Ben Franksen -
Brandon S. Allbery KF8NH -
brian -
C.M.Brown -
Dan Weston -
Daniel Fischer -
David Roundy -
Don Stewart -
Evan Laforge -
Gwern Branwen -
jeff p -
Jonathan Cast -
Ketil Malde -
Krzysztof Skrzętnicki -
Maarten Hazewinkel -
Matus Tejiscak -
Neil Mitchell -
Niels Aan de Brugh -
Robert Greayer -
Thomas Davie -
Thomas M. DuBuisson -
Tim Docker -
Tim Newsham