TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Destroy All Ifs – A Perspective from Functional Programming

362 点作者 buffyoda将近 9 年前

43 条评论

barrkel将近 9 年前
Define true as a lambda taking two lazy values that returns the first, and false as one that returns the second, and you can turn all booleans into lambdas with no increase in code clarity.<p>The straw man in the post - talking about a case-sensitive matcher that selectively called one of two different functions based on a boolean - is indeed trivially converted into calling a single function passed as an argument, but it&#x27;s hard to say that it&#x27;s an improvement. Now the knowledge of how the comparison is done is inlined at every call point, and if you want to change the mechanism of comparison (perhaps introduce locale sensitive comparison), you need to change a lot more code.<p>That&#x27;s one of the downsides of over-abstraction and over-generalization: instead of a tool, a library gives you a box of kit components and you have to assemble the tool yourself. Sure, it might be more flexible, but sometimes you want just the tool, without needing to understand how it&#x27;s put together. And a good tool for a single purpose is usually surprisingly better than a multi-tool gizmo. If you have a lot of need for different tools that have similar substructure, then compromises make more sense.<p>This is just another case of the tradeoff between abstraction and concreteness, and as usual, context, taste and the experience of the maintainers (i.e. go with what other people are most likely to be familiar with) matters more than any absolute dictum.
评论 #12108071 未加载
评论 #12107977 未加载
评论 #12108353 未加载
评论 #12108229 未加载
评论 #12107960 未加载
评论 #12109510 未加载
评论 #12108877 未加载
评论 #12107941 未加载
ufo将近 9 年前
I&#x27;m surprised that the article and none of the comments so far mentioned the &quot;Expression Problem&quot;: <a href="http:&#x2F;&#x2F;c2.com&#x2F;cgi&#x2F;wiki?ExpressionProblem" rel="nofollow">http:&#x2F;&#x2F;c2.com&#x2F;cgi&#x2F;wiki?ExpressionProblem</a><p>Basically, if you structure the control flow in object oriented style (or church encoding...) then its easy to extend your program with new &quot;classes&quot; but if you want to add a new methods then you must go back and rewrite all your classes. On the other hand, if you use if-statements (or switch or pattern matching ...) then its hard to add new &quot;classes&quot; but its very easy to add new &quot;methods&quot;.<p>I&#x27;m a bit disappointed that this isn&#x27;t totally common knowledge by now. I think its because until recently pattern matching and algebraic data types (a more robust alternative to switch statements) were a niche functional programming feature and because &quot;expression problem&quot; is not a very catchy name.
评论 #12108854 未加载
评论 #12109438 未加载
评论 #12108841 未加载
评论 #12108721 未加载
kazinator将近 9 年前
Problem is, a decision has to be made somewhere about <i>which</i> function to pass into that &quot;if-free&quot; block of code. The if-like decision has just moved elsewhere. That is a win if it reduces duplication: if a lambda can be decided upon and then used in several places, that&#x27;s better than making the same Boolean decision in those several places.<p>Programs that are full of function indirection aren&#x27;t necessarily easier to understand than ones which are full of boolean conditions and if.<p>The call graph is harder to trace. What does this call? Oh, it calls something passed in as an argument. Now you have to know what calls here if you want to know what is called from here.<p>A few days ago, there was this HN submission: <a href="https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=12092107" rel="nofollow">https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=12092107</a> &quot;The Power of Ten – Rules for Developing Safety Critical Code&quot;<p>One of the rules is: no function pointers. Rationale: <i>Function pointers, similarly, can seriously restrict the types of checks that can be performed by static analyzers and should only be used if there is a strong justification for their use, and ideally alternate means are provided to assist tool-based checkers determine flow of control and function call hierarchies. For instance, if function pointers are used, it can become impossible for a tool to prove absence of recursion, so alternate guarantees would have to be provided to make up for this loss in analytical capabilities. </i>
评论 #12109213 未加载
评论 #12108680 未加载
qwertyuiop924将近 9 年前
This is, as many commenters have noted, just another overzealous programming doctrine. Just like &#x27;GOTO considered harmful.&#x27;<p>Here&#x27;s the deal: if is a flow control primitive. Just like goto and while. If (heh) that primitive isn&#x27;t high-level enough to handle the problem you are facing, it is incumbent upon you as a programmer to use another, higher level construct. That construct may be pattern matching, it may be polymorphism (or any other form of type-based dynamic dispatch). It may be a function that wraps a complex chain of repeated logic, and is handed lambdas to execute based upon the result. It may, as in the article given here, be a funtion that is handed lambdas which apply or do not apply the transformation described.<p>The point is, there are many branch constructs, or features that can be used as branch constructs, in most modern programming languages. Use the one that fits your situation. And if that situation isn&#x27;t a that complex, that construct may be if.<p>Fizzbuzz using guards is the most clean and modifiable fizzbuzz that I&#x27;ve seen in Haskell.<p>Although now that I think about it, if you provide a function with a list of numbers...
评论 #12109218 未加载
white-flame将近 9 年前
This whole campaigned is misguided.<p>&quot;Bad IFs&quot; are a code smell, and they&#x27;re being scapegoated when the real problems are management demanding that simple hackish prototypes &amp; tests be deployed into production, management that doesn&#x27;t allow time for refactoring, and poor programmers who think that &quot;bad IFs&quot; are good code.<p>But the main site also doesn&#x27;t do any reasonable job of defining what a &quot;Bad IF&quot; even is.<p>The crux of the matter is that programmers need time to craft the details of a project to avoid or correct technical debt. These sort of reactions just point out one tiny portion of technical debt itself and doesn&#x27;t solve any fundamental problems at all.<p>(and yeah, I known I&#x27;m ranting against the Anti-IF campaign, not the particular take on the linked site. But this article just seems to parameterize the exact same parameters that are branched on anyway.)
评论 #12108088 未加载
评论 #12108335 未加载
Animats将近 9 年前
The idea that each type has its own control flow primitives is bothersome. It&#x27;s taken over Rust:<p><pre><code> argv.nth(1) .ok_or(&quot;Please give at least one argument&quot;.to_owned()) .and_then(|arg| arg.parse::&lt;i32&gt;().map_err(|err| err.to_string())) .map(|n| 2 * n) </code></pre> I&#x27;m waiting for<p><pre><code> date.if_weekday(|arg| ...) </code></pre> Reading this kind of thing is hard. All those subexpressions are nameless, and usually comment-less. This isn&#x27;t pure functional programming, either; those expressions can have side effects.
评论 #12108414 未加载
评论 #12108062 未加载
评论 #12109232 未加载
评论 #12108508 未加载
评论 #12107867 未加载
评论 #12116601 未加载
评论 #12108029 未加载
评论 #12108407 未加载
评论 #12108030 未加载
throwaway13337将近 9 年前
This just seems to obscure the logic. Not unlike how polymorphism can make code flow harder to read, though feel more clever.<p>There is a place for it - like when you&#x27;re trying to express a set of logic that will be guarded by the same condition, but always at the cost of some complexity.<p>A set of conditionals is probably the most obvious way to express branching.
评论 #12107805 未加载
评论 #12107955 未加载
dwrensha将近 9 年前
I recommend Bob Harper&#x27;s essay on &quot;boolean blindness&quot;: <a href="https:&#x2F;&#x2F;existentialtype.wordpress.com&#x2F;2011&#x2F;03&#x2F;15&#x2F;boolean-blindness&#x2F;" rel="nofollow">https:&#x2F;&#x2F;existentialtype.wordpress.com&#x2F;2011&#x2F;03&#x2F;15&#x2F;boolean-bli...</a><p>An excerpt:<p>&gt; The problem is computing the bit in the first place. Having done so, you have blinded yourself by reducing the information you have at hand to a bit, and then trying to recover that information later by remembering the provenance of that bit.
评论 #12108038 未加载
评论 #12108496 未加载
lilbobbytables将近 9 年前
Often times when I read about &quot;ideal&quot; ways of programming, I&#x27;m curious if it&#x27;s ever implemented in a production code base built by a team.
评论 #12108181 未加载
评论 #12108125 未加载
评论 #12109270 未加载
评论 #12108180 未加载
dahart将近 9 年前
I read everything I could find on the Anti-IF site and didn&#x27;t understand what the mission is exactly. They qualify and mention they want to remove the <i>bad</i> and <i>dangerous</i> IFs, but I couldn&#x27;t find examples that differentiate between bad ones and good ones -- are there good ones according to this campaign?<p>I like using functional as much as anyone, and removing branching often does make the code clearer and remove the potential for mistakes.<p>But I admit I have a hard time with suggesting people prefer a lambda to an IF, or to not ever use an IF. A lambda is, both complexity wise, and performance wise, <i>much</i> heavier than an IF. And isn&#x27;t is just as bad to abstract conditionals before any abstractions are actually called for?
评论 #12108295 未加载
externalreality将近 9 年前
I tried to ask the author the follow: (kept getting deleted as spam). Perhaps he will see it here but its unlikely due to the fact there are many comments as it is.<p>Hi John,<p>Are you familiar with Jackson Structured Programming?<p><a href="https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jackson_structured_programming" rel="nofollow">https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jackson_structured_programming</a><p>Notice how the focus in on using control flows that are derived from the structure of the data being processed and the processed data. Notice how the JSP derived solution in the Wikipedia example lack if-statements.<p>Pattern matching allows ones to map control flow to the structure of data. What are your thoughts on that? I think inversion of control has other benefits but I don&#x27;t think it has much to do with elimination of `if` conditionals, the pattern matching does that.<p>Also, I noticed one thing:<p>In the article you mention `doX :: State -&gt; IO ()` as being called for its value and suggest that if you ignore the value the function call has no effect. Isn&#x27;t it the case that a function of that type usually denotes that one is calling the function for its effect and not for any return value? Its value is just an unevaluated `IO ()`.
评论 #12109115 未加载
AYBABTME将近 9 年前
The author seems to ignore the fact that passing lambdas like this merely changes where the IF or SWITCH statement is made. I can agree that passing functions instead of booleans is better and more general. But pretending that IF&#x2F;SWITCH are thus avoided, is delusional.<p>For instance, at some point there will be a decision made whether the string matching must be case sensitive or not. If the program can do both at runtime, the IF will be, perhaps, in the main (or equiv.).
评论 #12108239 未加载
astazangasta将近 9 年前
Why don&#x27;t we just treat this like writing?<p>Good writing has one clear imperative: communicate meaningfully the intent of the author to the reader. Good code is no different; it is merely expressive writing in a different language, with, perhaps, greater constraint on its intent.<p>Some people make up rules like &quot;don&#x27;t use adverbs&quot;, or &quot;don&#x27;t split infinitives&quot;, in an effort to write better. But this doesn&#x27;t necessarily produce good writing; sometimes an adverb is just what you need.<p>The same is true of code. These are useful things to think about, but &quot;destroy all ifs&quot; is akin to &quot;never use a conjunction&quot;.
评论 #12108320 未加载
MrManatee将近 9 年前
If I understood correctly, the article suggests that as a general principle you should replace your union types and case-by-case code with lambdas. I feel almost the opposite.<p>Article: &quot;In functional programming, the use of lambdas allows us to propagate not merely a serialized version of our intentions, but our actual intentions!&quot;<p>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.<p>But let&#x27;s take instead the article&#x27;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 &quot;ß&quot; to match &quot;SS&quot;. 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.
jwatte将近 9 年前
The first problem is that the &quot;match&quot; function is considered in the first place. It&#x27;s too general. It should only be used in higher order constructs where its flexibility is actually needed.<p>Second: The enum based refractor is actually valuable and fine IMO. If you need string functions, stop there.<p>Now, shipping control flow as a library is a cool feature of Haskell. But, if those arguments are turned into functions, the match function itself isn&#x27;t needed! It just applies the first argument to arguments 3 and 4, then passes them to the second argument.<p>match :: (a -&gt; b) -&gt; (b -&gt; b -&gt; Bool) -&gt; a -&gt; b match case sub needle haystack = sub (case needle) (case haystack)<p>Does that even need to be a function? Perhaps. But if so, it&#x27;s typed in a and b and functions thereof, and no longer a &quot;string&quot; function at all. And, honestly, why are you writing that function?<p>Typing it out where you need it is typically less mental impact, because I don&#x27;t need to worry about the implementation of a fifth symbol named &quot;match.&quot;<p>sub (case needle) (case haystack)
galaxyLogic将近 9 年前
Isn&#x27;t this exactly the Smalltalk way? In ST what looks like if-statements actually are messages passed to instances of Boolean, with lambdas (in Smalltalk: BlockClosures) as argument. The boolean then makes the decision whether it will evaluate the lambda or not.
vittore将近 9 年前
When I read things like &quot;anti-if&quot; I recall this brilliant illustration that I saw several years ago - <a href="http:&#x2F;&#x2F;blog.crisp.se&#x2F;henrikkniberg&#x2F;images&#x2F;ToolWrongVsWrongTool.png" rel="nofollow">http:&#x2F;&#x2F;blog.crisp.se&#x2F;henrikkniberg&#x2F;images&#x2F;ToolWrongVsWrongTo...</a>
dozzie将近 9 年前
The inversion of control flow from called to calling function is an interesting way to describe (part of) functional programming style. I hadn&#x27;t thought of that this way, even though I use it for quite some time.
skybrian将近 9 年前
General principle: for every possible refactoring, the opposite refactoring is sometimes a good idea.<p>So, yes, replacing booleans with a callback is sometimes a good idea. But in other situations, replacing a callback with a simple booleans might also be a good idea.<p>Also, advice like this is often language-specific. In languages whose functions support named parameters, boolean flags are easy to use and easy to read. If you only have positional parameters, it&#x27;s more error-prone, so you might want to pass arguments using enums or inside a struct instead.
nialv7将近 9 年前
Someone found a hammer, and now everything looks like thumbs
nn3将近 9 年前
tl;dr: prefer callback hell instead of straight forward ifs and somehow that&#x27;s progress.
评论 #12109466 未加载
js8将近 9 年前
The idea that functional programming is a type of inversion of control reminds me of similar idea I had, when comparing OOP and FP.<p>In OOP, you encapsulate data into objects and then pass those around. The data themselves are invisible, they only have interface of methods that you can apply on them. So methods receive data as package on which they can call methods.<p>In FP, in contrast, the data are naked. But instead of sending them out to functions and getting them back, the reference frame is sort of changed; now the data stays at the function but what is passed around is the type of processing (another functions) you want to do with them.<p>For example, when doing sort; in OOP, we encapsulate the sortable things into objects that have compare interface, and let the sort method act on those objects. So at the time sort method is called, the data are prepared to be compared. In FP, the sort function takes both comparison function as an argument, together with the data of proper type; thus you can also look at it as that the generic sort function gets passed back into the caller. In other words, in FP, the data types <i>are</i> the interfaces.<p>So it is somewhat dual, like a different reference frame in physics.<p>The FP approach reminds me of Unix pipes, which are very composable. It stands on the principle that the data are the interface surface (inputs and outputs from small programs are well defined, or rather easy to understand), and these naked data are operated on by different functions (Unix commands). (Also the duality is kind of similar to MapReduce idea, to pass around functions on data in the distributed system rather than data itself, which probably explains why MapReduce is so amenable to FP rather than OOP.)<p>It also seems to me that utilizing this &quot;inversion of control&quot; one could convert any OOP pattern into FP pattern - just instead of passing objects, pass the function (method which takes the object as an argument) in the opposite direction.<p>I am not 100% convinced that FP approach is superior to OOP, but there are two reasons why it could be:<p>1. The &quot;nakedness&quot; of the data in FP approach makes composition much easier. In OOP, data are deliberately hidden from plain sight, which destroys some opportunities.<p>2. In OOP, what often happens is that you have methods that do nothing rather than pass the data around (encapsulate them differently). In FP approach, this would become very easy to spot, because the function that is passed in the other direction would be identity. So in FP, it&#x27;s trivial to cut through those layers.
评论 #12112154 未加载
asQuirreL将近 9 年前
The article seems to advocate type synonyms like the following:<p><pre><code> type Case = String -&gt; String -- ... type Announcer = String -&gt; IO String </code></pre> I would argue that these are actually much worse than not having type synonyms at all.<p>(String -&gt; String) functions could do anything to your query parameter and text, the type is too coarse, and the inhabitants too opaque for us to reason about them easily. Naming the type suggests the problem is solved without actually having solved it. It is like finding a hole in the ground, and covering it with leaves, so you don&#x27;t have to look at it anymore. You are literally making a trap for the next person to come this way.<p>In an ideal world you would be able to use refinements to say that you want any (f :: String -&gt; String) such that `toUpper . f = toUpper` but without such facilities, I think I may just settle for:<p><pre><code> newtype Case = CaseSensitive Bool </code></pre> Sometimes, your type really does only have two inhabitants.
评论 #12112113 未加载
Eliezer将近 9 年前
I thought the argument was going to be &quot;Conditionals are bad for running on GPUs.&quot;
oliv__将近 9 年前
<p><pre><code> It’s no wonder that conditionals (and with them, booleans) are so widely despised! </code></pre> They are?
评论 #12109138 未加载
yawaramin将近 9 年前
view-source:<a href="http:&#x2F;&#x2F;antiifcampaign.com&#x2F;" rel="nofollow">http:&#x2F;&#x2F;antiifcampaign.com&#x2F;</a><p>Find in page: &#x27;if(&#x27;<p>2 hits.<p>So, yeah.
true_religion将近 9 年前
This is the starter code:<p><pre><code> publish :: Bool -&gt; IO () publish isDryRun = if isDryRun then do _ &lt;- unsafePreparePackage dryRunOptions putStrLn &quot;Dry run completed, no errors.&quot; else do pkg &lt;- unsafePreparePackage defaultPublishOptions putStrLn (A.encode pkg) </code></pre> This would be nicer if you could do multiple functions with pattern matching. In Elixir this would be:<p><pre><code> @spec publish(boolean) :: any def publish(true = _isDryRun) do _ = unsafePreparePackage dryRunOptions IO.puts &quot;Dry run completed, no errors.&quot; end def publish(false = _isDryRun) do pkg = unsafePreparePackage defaultPublishOptions IO.puts (A.encode pkg) end </code></pre> Pattern matching is pretty powerful, even going as far to give a dynamic, non-statically types language like Elixir the ability to &#x27;destroy all iffs&#x27; too.
评论 #12108096 未加载
评论 #12108439 未加载
评论 #12108926 未加载
whazor将近 9 年前
Reducing if statements does shrink the possible state space, however using additional abstraction might increase it even further.
cdevs将近 9 年前
Bad programmers will mess any syntax restrictions&#x2F;guidelines&#x2F;styles we put on them. If you let them make any function were they can put launchNukes(); into doX(); then they will. though running things as a service may be the future, this launchNukes(); function is over here....safe from you.
VladKovac将近 9 年前
Functional programmers love to emphasize how all the aspects of programming that their pet language is uniquely good at dealing with also happen to be the biggest problems in code maintenance. Is there any actual data on what the biggest problem sources are?
评论 #12108314 未加载
svanderbleek将近 9 年前
I think pattern matching is fine, I don&#x27;t see how it is still &quot;boolean&quot;. The additional techniques shown are interesting, but heavy abstractions that should not be prescribed in general.
iopq将近 9 年前
This was the idea I used when I did<p><a href="https:&#x2F;&#x2F;bitbucket.org&#x2F;iopq&#x2F;fizzbuzz-in-rust&#x2F;overview" rel="nofollow">https:&#x2F;&#x2F;bitbucket.org&#x2F;iopq&#x2F;fizzbuzz-in-rust&#x2F;overview</a><p>I still had to bottom out at <a href="https:&#x2F;&#x2F;bitbucket.org&#x2F;iopq&#x2F;fizzbuzz-in-rust&#x2F;src&#x2F;9e5fcaabbd5f364839be929a974db0ac31d79c2e&#x2F;src&#x2F;lib.rs?at=master&amp;fileviewer=file-view-default#lib.rs-53:56" rel="nofollow">https:&#x2F;&#x2F;bitbucket.org&#x2F;iopq&#x2F;fizzbuzz-in-rust&#x2F;src&#x2F;9e5fcaabbd5f...</a>
smoothdeveloper将近 9 年前
Paul Blasucci had a good talk on Active Patterns (an F# language feature):<p><a href="https:&#x2F;&#x2F;github.com&#x2F;pblasucci&#x2F;DeepDive_ActivePatterns" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;pblasucci&#x2F;DeepDive_ActivePatterns</a><p>This feature allows to encapsulate conditional matching on arbitrary input and dispatching.<p>For those who know ML, it is making the concept of pattern matching extensible to any construct.
dorfsmay将近 9 年前
Since this is about FP, we have to have recursion:<p><a href="https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;functionalprogramming&#x2F;comments&#x2F;4t913u&#x2F;destroy_all_ifs_a_perspective_from_functional&#x2F;" rel="nofollow">https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;functionalprogramming&#x2F;comments&#x2F;4t91...</a>
mapleoin将近 9 年前
This is the best bit I think:<p>&gt; The problem is fundamentally a protocol problem: booleans (and other types) are often used to encode program semantics.<p>&gt; Therefore, in a sense, a boolean is a serialization protocol for communicating intention from the caller site to the callee site.
sebastianconcpt将近 9 年前
Less if is better, I agree on that. Lamdas technique is interesting because they &quot;encapsulete&quot; a specific case. In OOP this is achieved by using polymorphism on the objects instantiated for the right case. Right?
based2将近 9 年前
If &#x27;if&#x27; could support single &#x27;expression&#x27; and multiple &#x27;case&#x27;s like &#x27;switch&#x2F;match&#x27;, it would make easier the transition.
rosalinekarr将近 9 年前
only a sith speaks in absolutes
basicplus2将近 9 年前
sounds like what&#x27;s really being said is..<p>It is recommended that programmers use abstractions whenever suitable in order to avoid duplication, and associated errors
dingleberry将近 9 年前
i can&#x27;t think of use of &#x27;if&#x27; in a math function; however, if is implicitly used in input, say 0&lt;x&lt;1, f(x)=x, 1&lt;x&lt;3, f(x)=x^2<p>i see a lot of loop though, summation is so a double integral is loop within loop. i can&#x27;t think a code analogue with derivative<p>fta, i take that if in function body makes an ugly code.
评论 #12108624 未加载
评论 #12109959 未加载
rimantas将近 9 年前
Sandi Metz talks about ifs a bit here: <a href="https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=OMPfEXIlTVE" rel="nofollow">https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=OMPfEXIlTVE</a>
AWildDHHAppears将近 9 年前
You can go a long way without Ifs in a pattern-matching language like Prolog or Erlang, too.
sqldba将近 9 年前
Ummm. Many common day to day languages don&#x27;t use lambdas. Also I have no idea what they are. So - yeah I don&#x27;t think you can just replace if so easily.
评论 #12109109 未加载