6 ms·
Go repeats the mistake of Boolean Blindness [1] so I think it disqualifies it from "safe", though. [1] http://existentialtype.wordpress.com/2011/03/15/boolean-
by Peaker 14y ago
Go repeats the mistake of Boolean Blindness [1] so I think it disqualifies it from "safe", though.
[1] http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/ http://existentialtype.wordpress.com/2011/03/15/boolean-blin...
- drivebyacct2 14y agoYou posted this a few days ago, I didn't understand it and still don't understand after another read through. Can you give either a dumbed down summary or a scenario or an example of a language that doesn't do this?
- zemo 14y agothe last time he posted this was in response to me, so I'll join the discussion here to avoid duplication. This was his response when I asked for some clarification: >My problem with Go is that it does not have sum-types and pattern-matching. This means that branching (conditionals) in Go do not gain any type-information. And that means that programmers have to manually keep track of the invariants that hold true in each of their conditionals, and if they get them wrong, they get no help from the compiler.
- Peaker 14y agoBasically, the idea is that when you branch on a conditional, information is gained. This information may be represented in the type system and used by the compiler to verify safety, or it can be ignored. If it is ignored, the language is said to have "boolean blindness". Example: if (ptr == NULL) { ... a ... } else { ... b ... } In branch a and branch b, different invariants about ptr hold. But the language/compiler are not verifying any of these invariants. Instead, consider: data Maybe a = Nothing | Just a This defines a type "Maybe", parameterized by a type variable "a", and it defines two "data constructors" that can make a Maybe value: "Nothing" which contains nothing, and "Just" which contains a value of type "a" inside it. This is known as a "sum" type, because the possible values of the type are the sum of all data constructor values. We could still use this sum data-type in a boolean-blind way: if isJust mx then .. use fromJust mx .. -- UNSAFE! else .. can't use content of mx .. However, using pattern-matching, we can use it in a safe way. Assume "mx" is of type "Maybe a": case mx of Nothing -> ... There is no value of type "a" in our scope Just x -> ... "x" of type "a" is now available in our scope! So when we branch on the two possible cases for the "mx" type, we gain new type information that gets put into our scope. "Maybe" is of course a simple example, but this is also applicable for any sum type at all. If your language does branching without giving you back any new type information, it means you have to manually track the invariants and conditionals your program is in. If you get it wrong, your program will die at runtime. Instead, the language can almost always provide you some kind of proof that you can pass along of the conditional's truthness, which makes it safe to rely on it.
- zemo 14y agoI may be mistaken, but I believe type switches in Go address this criticism: type Fruit struct { Name string Species string } // define a method on Fruit structs named Rename func (f *Fruit) Rename(s string) { f.Name = s } type Candy struct { Name string Company string } // define a method on Candy structs named Rename func (c *Candy) Rename(s string) { c.Name = s } // any type that defines a method named Rename is also // of type Renamable. type Renamable interface { Rename(string) } // produce something that implements Renamable. Its // fields will be unknown, but the method Rename will be // known to exist. func SomeProducer(...) Renamable { ... } x := SomeProducer(...) // at this point, x is of type Renamable. We do not // know what fields it has. The only thing we know is // that it has some method named Rename that takes a // single string for its only argument and returns // nothing. switch t := x.(type) { case Fruit: // T is of type Fruit, while x is of type Renamable fmt.Print(t.Species) case Candy: // T is of type Candy, while x is of type Renamable fmt.Print(t.Company) default: // some other type. } it's in the Switch section of the Go spec: http://golang.org/ref/spec#Switch_statements http://golang.org/ref/spec#Switch_statements
- AnswerAndForget 14y agoNot exactly. You see, you have to define new types from scratch with your approach. With Haskell's approach, you just have to use the Maybe parametric type, which just needs to be defined once. In other words, you just go and use a generic type instead of writing your own, like new List<Integer> instead of new ListInteger in Java.
- zemo 14y agounless I'm missing something, that seems to address a separate difference in the specifics of Go and Haskell's type systems, namely that Go's interfaces and Haskell's sum types are different. Yes, they're different. I really only meant to say that the article is predicated on the notion that we're only able to branch on booleans instead of predicates. As for checking for null values... var x *MyType ... switch x { case nil: // blah default: // blah } tada, no boolean required, no having to "establish the provenance" of any bits. Sure, inside the nil case, the compiler doesn't actually stop you from unsafely attempting to access the fields of x, and, in that way, Haskell is safer than Go. I'm not trying to assert that Go is better than Haskell or that they are the same; I've never programmed Haskell so I'm not qualified to make such a statement. I'm merely addressing the specific "Boolean Blindness" criticism. I don't really think that criticism is in any way valid, since "Boolean Blindness" isn't a criticism of a language design, it's a criticism of how one might use a particular language. To say that Go committed "Boolean Blindness" is predicated on the following assumptions: - you can only branch on a boolean - given a value and a type, the only thing you can do is obtain a boolean indicating whether or not that value is of that type as far as Go is concerned, those statements are factually inaccurate. Given some variable `x` of unknown type, and a type `t`, is there some way, in Haskell, to create a boolean `v` such that the value of `v` is `true` when `x` is of type `t` and `false` otherwise? And if so, is there some way to branch on this boolean value `v`? Because if that is true, then how has Haskell not committed the same atrocity of "Boolean Blindness"?