Earlier quoted context omitted.
Not all control-flow primitives are necessary. Eg Haskell and Scheme get by without 'while' and 'goto'. Haskell would do just fine without a built-in 'if': you can define 'if' as a function via pattern matching. Given that perspective, the article would be a call to use more expressive types than Booleans to match on---and in lots of cases not to match at all, but provide what would be the result of the match as an a…
Scheme and Haskell have other primitives that take the place of while and goto. But yes, using more expressive match types or parameters is a good idea. As for providing the result as an argument, that can be a good pattern, but isn't always practical. Note what I said in my original comment about using your own discretion.
Destroy All Ifs – A Perspective from Functional Programming
211–220 of 226 posts
Re: Destroy All Ifs – A Perspective from Functional Programming
#212Earlier quoted context omitted.
Tee hee, Haskell doesn't have tail recursion (e.g. foldl takes linear space), and structural recursion in Haskell isn't guaranteed to terminate (e.g. if you're given an infinite list). If I were in charge of developing a safety critical system, and someone came to me with a proposal to write it in Haskell, I'd be very skeptical.
??? Haskell absolutely has tail recursion; foldl just evaluates non-strictly and therefore can leave thunks in memory. This is fine for e.g. reversing a cons-list. Regardless, it is tail recursive (and uses constant stack space). foldl' is also tail recursive and has strict semantics. Structural recursion can't be guaranteed to terminate in any language that supports codata unless you have some sort of totality check…
Definitely.
I something think Haskell should treat non-terminating code the same way they treat IO: only allow it in portions of the code-base that are tagged somehow.
If there's something that doesn't cause a side-effect, but the compiler doesn't know that, you can tell it with unsafePerformIO. If there's something that terminates, but the compiler doesn't know, (eg calculating the hailstone sequence for a number), there could also be a suitable escape hatch to be used with care.
Re: Destroy All Ifs – A Perspective from Functional Programming
#213Article: "In functional programming, the use of lambdas allows us to propagate not merely a serialized version of our intentions, but our actual intentions!"
Counterpoint: The use of structured objects instead of black box lambdas allows us to do more than just evaluate them. For example, Redux gets a lot of power by separating JSON-like action objects from the reducer that carries out the action.
But let's take instead the article's example of case-insensitive string matching. One tricky case is that normalization can change the length of the string: we might want the german "ß" to match "SS". Sure, the lambda approach can handle that. But now suppose that we want a new function that gives the location of the first match. It should support the same case-sensitivity options (because why not?). But now there is no way to get the pre-normalization location, because we encoded our normalization as a black box function. Case-by-case code would have handled this easily.
Re: Destroy All Ifs – A Perspective from Functional Programming
#214Earlier quoted context omitted.
Granted, I'm a mostly self-taught programmer, but I would have thought that if something appears in formal logic,[0] it should have an analog in a programming language. Even standard algorithms like quicksort[1] use conditionals. And, while I can see how massive switch statements suck, normal conditionals are common in everyday life: "If they don't have a dark roast coffee, get me a medium roast." All of which is to…
> normal conditionals are common in everyday life: "If they don't have a dark roast coffee, get me a medium roast." "They had dark roast so I got you nothing as requested." IOW, this program is either incomplete or wrong. Cf. "Get me the darkest roast they have." - ifless, concise, robust.
Re: Destroy All Ifs – A Perspective from Functional Programming
#215Earlier quoted context omitted.
Scheme and Haskell have other primitives that take the place of while and goto. But yes, using more expressive match types or parameters is a good idea. As for providing the result as an argument, that can be a good pattern, but isn't always practical. Note what I said in my original comment about using your own discretion.
They don't have `other primitives': they have function calls. Most languages have function calls these days.
Re: Destroy All Ifs – A Perspective from Functional Programming
#216Earlier quoted context omitted.
??? Haskell absolutely has tail recursion; foldl just evaluates non-strictly and therefore can leave thunks in memory. This is fine for e.g. reversing a cons-list. Regardless, it is tail recursive (and uses constant stack space). foldl' is also tail recursive and has strict semantics. Structural recursion can't be guaranteed to terminate in any language that supports codata unless you have some sort of totality check…
The point of tail recursion is using constant space, not constant stack space (does Haskell even have a stack?) Anyways, the Haskell spec allows foldl' to use linear space just like its lazier counterparts. The fact that it uses constant space is an implementation detail of GHC. Reference: https://github.com/quchen/articles/blob/master/fbut.md#seq-d... Structural recursion always terminates in SML. Supporting infinit…
Re: Destroy All Ifs – A Perspective from Functional Programming
#217Earlier quoted context omitted.
That coding guideline simply rejects all recursion, even cases that are correct by inspection, or by easy proof.
Interesting. I assume they allow the special cases of tail recursion introduced by 'while', 'for' and similar constructs?
Re: Destroy All Ifs – A Perspective from Functional Programming
#218The idea that each type has its own control flow primitives is bothersome. It's taken over Rust: argv.nth(1) .ok_or("Please give at least one argument".to_owned()) .and_then(|arg| arg.parse:: ().map_err(|err| err.to_string())) .map(|n| 2 * n) I'm waiting for date.if_weekday(|arg| ...) Reading this kind of thing is hard. All those subexpressions are nameless, and usually comment-less. This isn't pure functional progra…
Re: Destroy All Ifs – A Perspective from Functional Programming
#219The idea that each type has its own control flow primitives is bothersome. It's taken over Rust: argv.nth(1) .ok_or("Please give at least one argument".to_owned()) .and_then(|arg| arg.parse:: ().map_err(|err| err.to_string())) .map(|n| 2 * n) I'm waiting for date.if_weekday(|arg| ...) Reading this kind of thing is hard. All those subexpressions are nameless, and usually comment-less. This isn't pure functional progra…
Oh god it's like ActionSupport came back with a vengeance.
Re: Destroy All Ifs – A Perspective from Functional Programming
#220Earlier quoted context omitted.
The stance is rather different though - "GOTO Considered Harmful" as a phrase is both inviting a discussion and making a limited statement. "Destroy all ifs" is definitive; the argument is over at the end of the phrase and there will be no negotiation or concessions. I know that this is trivial in this case, but I think it would help discourse in the world generally if we could move away from this kind of position ta…
"Ifs considered suboptimal" carries the spirit of Dijkstra and the general argument of the anti if folks.