5 ms·
>Anything you could unit test without mock objects You can unit test without mock objects because you're following functional patterns.
by carlmr 3y ago
>Anything you could unit test without mock objects
You can unit test without mock objects because you're following functional patterns.
- taeric 3y agoNot necessarily. For an easy counter example, build a Sierpiński triangle. Easy to unit test. Easier to build using imperative code than functional. (This goes for a ton of fractals, honestly.)
- PaulHoule 3y agoIt burns me up that Fibonacci numbers are used so frequently as an example of functional programming because it is a clear case of malpractice, particularly because it performs terribly without memoization. Even in the 1980s CS profs were trying to tell us how BASIC sucks but efficient Fibonacci is so easy to code up in BASIC. (I'd really be impressed with a system that could figure out the closed form based on the definition.) How easy it is to draw fractals depends on your tools. If you have turtle graphics with affine transformations (rotation and scaling) it is so easy to write simple recursive programs to draw space filling curves and such.
- taeric 3y agoChange for a dollar is another fun one there. Closed form is mind bending, and I don't think we have built a system that can derive that.
- dragonwriter 3y ago> It burns me up that Fibonacci numbers are used so frequently as an example of functional programming because it is a clear case of malpractice, particularly because it performs terribly without memoization It's used as an example of basic recursive programming largely because its an example that can be returned to with memoization. That there is a better way to do it that can be shown later is a reason for selecting early examples in a curriculum.
- JoelMcCracken 3y agoOr you can write the tail-recursive version which starts from 1 and builds to n; no need to memoize.
- toast0 3y agoI don't see why you can't write the closed form in a functional language as well. This only works for 1 =< N =< 70, for N >= 71 the approximation of Sqrt5 isn't good enough; you need to do more symbolic math so you can eliminate that term. -module('fib'). -export([go/1]). go(N) when N < 1 -> throw(badarg); go(N) -> Sqrt5 = math:sqrt(5), round(1.0 / Sqrt5 * math:pow((1 + Sqrt5) / 2.0, N) - 1.0 / Sqrt5 * math:pow((1 - Sqrt5) / 2.0, N)). rp(lists:map(fun fib:go/1, lists:seq(1, 71))). [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584, 4181,6765,10946,17711,28657,46368,75025,121393,196418, 317811,514229,832040,1346269,2178309,3524578,5702887, 9227465,14930352,24157817,39088169,63245986,102334155, 165580141,267914296,433494437,701408733,1134903170, 1836311903,2971215073,4807526976,7778742049,12586269025, 20365011074,32951280099,53316291173,86267571272, 139583862445,225851433717,365435296162,591286729879, 956722026041,1548008755920,2504730781961,4052739537881, 6557470319842,10610209857723,17167680177565,27777890035288, 44945570212853,72723460248141,117669030460994, 190392490709135,308061521170130] ( 190392490709135 + 117669030460994 /= 308061521170130 )
- taeric 3y agoThe question is if you can have a system that can derive the closed form from the definition. Not one you can feed it to. Right? Similarly, it may be faster to use the repeated squares approach to get a large value fib, and I don't know any system that can get that from the recursive definition, either.
- toast0 3y agoNonfunctional programming doesn't give you that either, so I don't see the problem? If you use the iterative / recursive form, runtime performance isn't great. If you use a lookup table, performance is great, but space might suffer (but then, how many values do you really need?). If you want to use something better, you've got to write it yourself. Maybe one day an optimizing compiler will figure it out, but it's more likely to figure out you never call it with N > 10, and precompute, and that's fine.
- taeric 3y ago
- shhsshs 3y ago> Easier to build using imperative code than functional. I am a bit confused about this. The following code is pure and I can't think of an easier way to do it imperatively: function triangle(n) { if (n <= 1) { return [1] } const p = triangle(n - 1) return Array.from({ length: n }) .map((_, i) => (p[i - 1] ?? 0) + (p[i] ?? 0)) }
- taeric 3y agoI was actually mildly curious if anyone would call me on having picked an easy fractal to do with the recursion. :) When I wrote the post, I was thinking of the Koch curve. Not sure why I mixed it up in name with the other. I was definitely leaning on some of the abstractions that you get in the likes of turtle geometry to show how this works. Many of the programs you can write using those primitives are very interesting, and far more unwieldy in any other paradigm. At large, I view it as the difficulty of forcing Cartesian coordinates. For a large number of problems, things get far easier to state in polar coordinates. Such that your choice of abstraction and representation matters. A lot.
- PaulHoule 3y agoAnother "pure functional" approach to drawing these things is to write a shader program which takes the (x,y) coordinates and returns the pixel values. Here is a parameterized Koch curve done as a shader https://www.shadertoy.com/view/XdcGzH https://www.shadertoy.com/view/XdcGzH
- PaulHoule 3y ago... or working in an environment or on a problem for which functional patterns apply. Suppose you are writing a "CRUD" app that writes to a relational database, how do you apply functional programming to that? The whole point of an application like that is that it makes side effects. In some cases you can break those problems down into functional pieces. Consider Python drivers for a product like https://www.arangodb.com/ https://www.arangodb.com/ One major problem is that you want drivers that work synchronously and asynchronously, the structure of the average api call is something like def query(parameters): encoded_parameters = encode_parameter(parameters) url = generate_rest_url(configuration, parameters) response = do_post(url, encoded_parameters) return decode_response(response) To make that async all you have to do is stick "async" before the def and put an "await" before the do_post. 95% of the API calls have a structure like the above. Most of the complexity lives in encode_parameter(), generate_rest_url() and decode_response() are all perfectly functional functions that are easy to test. Code gen the API stubs and you get sync and async switching almost for free. Contrast that to the option of mocking do_post.
- SoftTalker 3y agoI never really understood what a "monad" is but for a program like that I'd have all the "side effect" stuff separated from all the "data manipulation" stuff. It could all be organized as functions, which I generally do because I find it much easier to reason about mentally than objects. "Pure" functional programming is an extreme which probably isn't suitable for many real-world programming tasks. Functional style is typically what I do, I use lists and maps and folds quite a bit but still end up updating a database.
- PaulHoule 3y agoCode like the above can be restructured with monads in various ways. In general there is the pattern of building systems that do execution control such as the methods used to create control structures in Lisp, the Executor in Java, etc. One way to use monads is to accumulate a list of operations that need to be done and actually does the operations when you "unpack" the monad at the end. Such a monad could do many of the things a compiler does since it has the operation list at the outset and doesn't just blast from one statement to the next. This blends into the techniques used in Fluent DSLs.
- ashishmax31 3y agoCould you elaborate a little bit more? These days i write a lot of go code and i end up using mock objects for different interfaces to write unit tests. Is there another way to do this?