You can unit test without mock objects because you're following functional patterns.
You can unit test without mock objects because you're following functional patterns.
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
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."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.
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.
That way you don't need to Mock, but you can input a "connection" during testing that does what you expect.
If you say "well now I have to type extra parameters everywhere", this is usually solved with partial application. I.e. you create findings from your functions that have the Db Connection already baked inside.
https://fsharpforfunandprofit.com/posts/low-risk-ways-to-use...
https://fsharpforfunandprofit.com/posts/partial-application/
However what you might want to do (and this is based on IIRC!) is have a typeclass for your database interaction. Then the real DB type and the mock one can both implement that typeclass and you have basically dependency injection.
There are other ways like free monads but I never got my head around that.
You could also create a monad transformer which is a bit more straight forward. They let you compose monads, so you can chain little bits of side effect. Like “here is something that can talk to a database and write logs and that is all it will do” … but actually it is pure! It thinks it is doing those things but they can be mocked.
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.
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.
-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 )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.
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.
This goes back to a ton of the talk on early compilers and systems. They would push that you should lean heavily on the system to move from definitions to optimal solutions. (This later got changed heavily to leaning only on the compiler, but the same idea applies.)
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))
}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.
I'm going to guess that the shader version is a bit larger than the naive turtle version. Not that it isn't doable, but is tough evidence to hold for the benefits of the expressive power of functional code. :D
It doesn’t look like “functional code” but it really is since the shader returns a pixel color for x and y. I bet there is some squarish kind of fractal that could be code golfed with but manipulation.
At any rate, thanks for the link! Will try another computer later to see if it can load.