Functional programming design for identity and side effect

Does functional programming have a standard design for this logic?

const passAround = (f) => (x) => { f(x); return x; }; 

This allows me to create functions that have side effects and do not return values, like console.log . This does not seem like a task, because I do not want to represent a side effect state.

+5
source share
3 answers

Anyway, the reason why I bring up SKI combinatorial computation is because I want to introduce you to Applicative Functors . In particular, the application functor Reader is equal to the equivalent for computing the SKI combinator. The combinator S equivalent to the ap Reader method, and the combinator K equivalent to the pure Reader method.

In JavaScript, the equivalent of Reader is Function . Therefore, we can define ap and pure for functions in JavaScript as follows:

 Function.prototype.ap = function (f) { return x => this(x)(f(x)); }; Function.pure = x => y => x; const print = Function.pure.ap(console.log); console.log(print(10) + 20); 

But wait, there is so much more you can do with applicative functors. Each applied functor is also a functor. This means that applicative functors must also have a map method. For Reader the map method is just a mix of functions . This is equivalent to combinator B Using map , you can do really interesting things, for example:

 Function.prototype.ap = function (f) { return x => this(x)(f(x)); }; Function.pure = x => y => x; const id = x => x; // I combinator of SKI combinator calculus Function.prototype.map = function (f) { return x => this(f(x)); }; Function.prototype.seq = function (g) { return Function.pure(id).map(this).ap(g); }; const result = console.log.seq(x => x + 20); console.log(result(10)); 

The seq function is actually equivalent to the (*>) method of the Applicative class. This allows you to use the functional style of the cascading method .

+5
source

If you are talking about purely functional programming, you need to challenge this starting point:

which have side effects and do not return values

In functional programming, there is no such thing. Each function is defined as a conversion at some input to some output.

So the obvious question is: how would you imagine console.log without a side effect? To answer, we need to challenge another assumption in your question:

I do not want to present a side effect state

This is how functional programming poses a problem: consider your input and output as "state of the world." In other words, given the state of the world before the function, return the state of the world after the function. In this case, you represent the state of the console: using the console with x lines of output, you get a console with x + 1 lines of output. Roughly, you could write something like this:

 (x, console) => { return [x, console.withExtraLine(x)]; } 

The more powerful mechanism commonly used to represent this is called the "monad" - a special kind of object that wraps a series of steps along with some additional meaning. In the case of the IO monad, each step ends with an action that transforms the state of the world. (I / O is just one of many useful applications of the concept of monad.)

You write steps as functions that know only about the "expanded" value of any part of this state (for example, a parameter that ultimately came from user input), and the monad processes the messy details of the actual execution of this outside the scope of the functional program. Therefore, instead of thinking of your input and output as a "state of the world", you think of your input as a "calculation chain", and your result is a "slightly longer calculation chain."

There are many comments on this, which are much better than I could give, just looking for "monad" or "functional programming io".

See also this answer , this question, and possibly many others in the "Related" sidebar, is automatically generated when viewing this question.

+5
source

So, in Haskell terminology, you want:

 passAround :: Monad m => (a -> mb) -> a -> ma passAround fx = do fx return x 

Read the type signature because " passAround accepts a function f :: a -> mb , the result of which is a monadic action (that is, that can have side effects that can be ordered in a specific order, thus Monad m ) with an arbitrary type result b and value a to pass this function. This gives a monadic action with result type a . "

To find out what this may mean, โ€œfunctional programming construct,โ€ let us first expand this syntax. In Haskell, the notation for do is simply syntactic sugar for monadic combinators, namely

  do foo bar 

- sugar for foo >> bar . (This is a little trivial, it all really gets interesting when you also bind local results to variables.)

So,

 passAround fx = fx >> return x 

>> in itself is an abbreviation of the general operator of a monadic chain, namely

 passAround fx = fx >>= const (return x) 

or

 passAround fx = fx >>= \y -> return x 

(This backslash denotes a lambda function, in JavaScript it will read f(x) >>= (y)=>return x .)

Now, what you really want all this is a chain of several actions. In Javascript, you write g(passAround(f, x)) , in Haskell this is not just a function argument, because it is still a monadic action, so you want to use another monadic chaining operator: g =<< passAround fx or

 passAround fx >>= g 

If we expand passAround here, we get

 (fx >>= \y -> return x) >>= g 

Now, we can apply the laws of the monad , namely the law of associativity, giving us

 fx >>= (\y -> return x >>= g) 

and now the left unit law

 fx >>= (\y -> gx) 

IOW, the whole composition is minimized down to fx >> gx , which can also be written

  do fx gx 

... which is kind, duh. What of all this? Good, good, that we can abstract from this monadic rewriting with a monad transformer. In Haskell, it is called ReaderT . What would you do if you know that f and g use the variable x , you can exchange

 f :: a -> mb g :: a -> mc 

with

 f' :: ReaderT amb f' = ReaderT f g' :: ReaderT amc g' = ReaderT g 

The ReaderT value ReaderT conceptually matches your passAround function.

Note that ReaderT amc has the form (ReaderT am) c or, ignoring the details, m' c , where m' again a monad! And using the do syntax for this monad, you can simply write

  runReaderT (do f' g' ) x 

which in JavaScript would theoretically look like

  runReaderT (() => { f'; g'; }, x) 

Unfortunately, you cannot actually write it like this, because unlike Haskell, imperative languages โ€‹โ€‹always use the same monad to sequence their work (which roughly corresponds to the Haskell IO monad). By the way, this is one of the standard descriptions of what a monad is: it is an overloaded comma operator .

However, what you can certainly do is implement the monad transformer on dynamic types in the functional part of the JavaScript language. I'm just not sure if this is worth doing.

+2
source

Source: https://habr.com/ru/post/1271641/


All Articles