Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have checked the possibility of duplicate question, and cannot find the exact solution.

I wrote some function chain code in JavaScript as below, and works fine.

var log = function(args)
{
  console.log(args)

  return function(f)
  {
    return f;
  };
};

(log('1'))(log('2'))(log('3'))(log('4'));

//1
//2
//3
//4

I want to make this lazy evaluation.

Or to compose function.

var log = function(args)
{
  var f0 = function()
  {
    return console.log(args);
  };

  return function(f1)
  {
    return function()
    {
      f0();
      return f1;
    };
  };
};

var world = (log('1'))(log('2'))(log('3'))(log('4'));
console.log(world);
//should be just a function,
// but in fact
//1
//[function]

world();
//should be
//1
//2
//3
//4

// but in fact
// 2

Something is very wrong. Can you fix it?

Thanks.

This question is resolved, but there is further

async issue as shown in comment discussion

When we have

// unit :: a -> IO a
var unit = function(x)
{
  return function()
  {
    return x;
  };
};

// bind :: IO a -> (a -> IO b) -> IO b
var bind = function(x, y)
{
  return function()
  {
    return y(x())();
  };
};

// seq :: IO a -> IO b -> IO b
var seq = function(x, y)
{
  return function()
  {
    return x(), y();
  };
};

var action = function(x)
{
  return function(y)
  {
    return y ? action(seq(x, y)) : x();
  };
};

var wrap = function(f)
{
  return function(x)
  {
    return action(function()
    {
      return f(x);
    });
  };
};

var log = wrap(console.log);



// -- runtime -- 
// HACK: when `world` is modified by passing a function,
//       the function will be executed.

Object.defineProperties(window,
{
  world:
  {
    set: function(w)
    {
      return w();
    }
  }
});

We also often want async chain reactions badly.

var asyncF = function(callback)
{
  setTimeout(function()
  {
    for (var i = 0; i < 1000000000; i++)
    {

    };

    callback("async process Done!");
  }, 0);
};

var async = wrap(asyncF(function(msg)
{
  world = log(msg);

  return msg;
}));

Now,

world = (log(1))(async)(log(3));
//1
//3
//async process Done!

So far nice and smooth, now we try to use bind

world = (log(1))
  (bind((async), (log(x))));

//should be
//1
//async process Done!
//3

//in fact
//ReferenceError: x is not defined

Could you modify to make this work, please?

one more about retrun x, y; multiple value

I don't understand

  // seq :: IO a -> IO b -> IO b
    var seq = function(x, y)
    {
      return function()
      {
        return x(), y();
      };
    };

as the library author mentions

Note that this is not possible in Haskell because one function can't return two results. Also, in my humble opinion, it looks ugly.

I agree, and don't know what this

return x(), y();

multiple return value.

I googled and searched here, but could not find an answer.

What is this??

(just in case, I will chose this hack for the syntax)

Thanks!

share|improve this question
    
Do you want currying? –  Florian Margaine Jan 16 at 9:58
    
No, it is not currying. It's basically to chain any wrapped functions, like Haskell IO monad. –  Ken OKABE Jan 16 at 10:00
1  
I have a very naive doubt, (log(1)) will be evaluated before (log(4)) during evaluating var world = (log('1'))(log('2'))(log('3'))(log('4'));, right? –  Anurag Peshne Jan 16 at 10:21
1  
The seq function does not return multiple values. In JavaScript when you write expressionA <comma> expressionB the interpreter first evaluates expressionA, ignores its result, and then evaluates expressionB. The entire expression evaluates to the result of expressionB. For example, 1, 2 evaluates to 2; and alert("Hello World!"), (1 + 2) evaluates to 3 after alerting Hello World!. As MDN puts it: the comma operator evaluates each of its operands (from left to right) and returns the value of the last operand. seq :: IO a -> IO b -> IO b ignores IO a. –  Aadit M Shah Jan 17 at 2:54
1  
When I said that "this is not possible in Haskell because one function can't return two results", I was referring to the action function, not the seq function. The action(x) returns either x or else seq(x, y) (wrapped in a superfluous function for laziness) depending upon whether or not you pass the argument y. It is not possible to implement the action function in Haskell because in Haskell functions when supplied with the same arguments must return the same results. It is possible to implement seq in Haskell (it is called >> in Haskell). action is a hack. Don't use it. –  Aadit M Shah Jan 17 at 3:04

4 Answers 4

up vote 24 down vote accepted

So if I understand the question correctly, you want to chain IO actions in JavaScript. To do so, you first need to define what an IO action is. One way to think of an IO action is that it is simply a function which takes no arguments. For example:

// log :: a -> IO b

function log(x) {
    return function () {       // IO action
        return console.log(x);
    };
}

One advantage of representing an IO action as a function with no arguments is that it is the same representation for thunks (unevaluated expressions). Thunks are the things that enable lazy evaluation in languages like Haskell. Hence you get laziness for free.

Now composition. How do you compose two IO actions in JavaScript? In Haskell, you use the >> operator to sequence IO actions, which is usually defined in terms of >>= (a.k.a. bind) as follows:

(>>=) :: Monad m => m a -> (a -> m b) -> m b

(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= \_ -> y

It is easy to write an equivalent bind function for our IO actions in JavaScript:

// bind :: IO a -> (a -> IO b) -> IO b

function bind(x, y) {
    return function () {
        return y(x())();
    };
}

Suppose you have an IO action x :: IO a. Since it's just a function with no arguments, when you call it it's equivalent to evaluating the IO action. Hence x() :: a. Feeding this result to the function y :: a -> IO b results in the IO action y(x()) :: IO b. Note that the entire operation is wrapped in a superfluous function for laziness.

Similarly, it is just as straightforward to implement the >> operator. Let's call it seq as in “sequence”.

// seq :: IO a -> IO b -> IO b

function seq(x, y) {
    return function () {
        return x(), y();
    };
}

Here we evaluate the IO expression x, don't care about its result and then return the IO expression y. This is exactly what the >> operator does in Haskell. Note that the entire operation is wrapped in a superfluous function for laziness.

Haskell also has a return function which lifts a value into a monadic context. Since return is a keyword in JavaScript, we'll call it unit instead:

// unit :: a -> IO a

function unit(x) {
    return function () {
        return x;
    };
}

As it turns out there's also a sequence operator in Haskell which sequences monadic values in a list. It can be implemented in JavaScript for IO actions as follows:

// sequence :: [IO a] -> IO [a]

function sequence(array) {
    return function () {
        var list   = array;
        var length = list.length;
        var result = new Array(length);
        var index  = 0;

        while (index < length)
            result[index] = list[index++]();
        return result;
    };
}

That's all we need. Now we can write:

var world = sequence([log("1"), log("2"), log("3"), log("4")]);

world();

// 1
// 2
// 3
// 4

Hope that helps.


Yes, it is indeed possible to chain IO actions using your syntax. However, we'll need to redefine what an IO action is:

function action(x) {
    return function (y) {
        return y ? action(seq(x, y)) : x();
    };
}

Let's understand what the action function does using an example:

// log :: a -> IO b
// log :: a -> IO r -> IO r

function log(x) {
    return action(function () {
        return console.log(x);
    });
}

Now you can do:

log("1")();         // :: b
log("1")(log("2")); // :: IO r

In the first case we evaluated the IO action log("1"). In the second case we sequenced the IO actions log("1") and log("2").

This allows you to do:

var world = (log("1"))(log("2"))(log("3"))(log("4"));

world();

// 1
// 2
// 3
// 4

In addition you can also do:

var newWorld = (world)(log("5"));

newWorld();

// 1
// 2
// 3
// 4
// 5

And so on....

Everything else remains the same. Note that this is not possible in Haskell because one function can't return two results. Also, in my humble opinion, it looks ugly. I prefer using sequence instead. However, this is what you wanted.

share|improve this answer
1  
@KenOKABE I updated my answer showing how it's possible to chain IO actions the way you want. Also, sequence is not like map. It only looks like map in JavaScript because of the way we are representing IO actions. –  Aadit M Shah Jan 16 at 11:46
2  
This is one of the greatest answer I have ever seen in stackoverflow. You are truly great. I confirmed your code works flawlessly, and surely satisfy all my intention. Also, this answer is extremely valuable to be refereed by all other people who study functional programming. Really appreciated. Thanks!!!! –  Ken OKABE Jan 16 at 12:51
3  
nice................... –  Naeem Shaikh Jan 16 at 13:04
1  
The only misleading bit here is the fact of that synchronous IO is performed here where most IO in JS is asynchronous which would require the inner functions to return promises for instance. –  Benjamin Gruenbaum Jan 16 at 15:22
1  
@AaditMShah you would require something similar to the IO monad, essentially a Promise with a then (bind, >>=) operation. –  Benjamin Gruenbaum Jan 16 at 17:08

Let's look at what happens here:

var log = function(args)
{
  var f0 = function()
  {
    return console.log(args);
  };

  return function(f1)
  {
    return function()
    {
      f0();
      return f1;
    };
  };
};

And inline just a bit:

var log = function(args) {
  return function(f1) {
    return function() {
      console.log(args);
      return f1;
    };
  };
};

So we're returning a function f which accepts a function f1, and returns a function g which does logic and returns f1. Quite a mouthful! Your question is why does

(log('1'))(log('2'))(log('3'));

Log 1. I did away with the log('4') since going to 3 is enough to show your described case. To answer that let's play compiler and do the inlining game!

(log('1'))(log('2'))(log('3'))
// =>
(
  function (f1) {
    return function () {
      console.log('1');
      return f1;
    }
  }
)(
  function (f1) {
    return function () {
      console.log('2');
      return f1;
    }
  }
)(
  function (f1) {
    return function () {
      console.log('3');
      return f1;
    }
  }
)

Simple substitution. I took each instance of log(something), replaced it with the contents of the function, replaced the argument with the value passed. Let's do it again!

(
  function () {
    console.log('1');
    return function (f1) {
      return function () {
        console.log('2');
        return f1;
      }
    };
  }
)(
  function (f1) {
    return function () {
      console.log('3');
      return f1;
    }
  }
)

This one is a bit trickier to follow: I expanded the first function call. The topmost function received an argument f1 to which we just supplied a value, so I went into the function and replaced every occurrence of f1 with the given value (the result of log('2')), just like with the log argument.

Look over what happened here again if you still don't follow, but my suggestion is doing it yourself: Copy the snippet into your favourite code editor and do the expansion yourself.

Now you may see why the log('1') was called. The next thing we, the compiler, need to do is take care of the next function call. And whadya know, the first line in that function is a console.log! Better do it!

What can we do!?

I don't know Haskell or the IO Monad, but as you currently planned, I do not think you can do what you want with basic functions, not like that. If you can say what problem you want to solve using this...erm...pattern, maybe we can help!

share|improve this answer
    
nice explanation: I couldn't explain like that :) –  Naeem Shaikh Jan 16 at 10:39
    
Well thanks 2 of you. The problem I want to solve is what you see, nothing more. Any application usage is based on this. It's not the optimization for a specific application usage. Thanks again. –  Ken OKABE Jan 16 at 10:43
    
To make this short. function composition. I will add this to my question. –  Ken OKABE Jan 16 at 10:51
    
@NaeemShaikh Hey Zirak. Perhaps my answer above will help you understand what the OP is asking. Also, the IO monad is just a data structure which allows one to write impure code in a pure language like Haskell. However, for impure languages like JavaScript there's no need for the IO monad because everything is impure anyway. Thus, essentially everything is in the IO monad because you can do IO any time you want. Hence, I'm not sure why the OP wants to implement the IO monad in JavaScript. Perhaps it's just a didactic exercise. I explained how the OP's problem can be solved in my answer above –  Aadit M Shah Jan 16 at 12:52

While executing

var world = (log('1'))(log('2'))(log('3'))(log('4'));

(log('1')) is executed first which returns a function which takes in (log('2')).

This anonymous function starts executing but doesn't accept any argument. log('3') is neglected. This can verified by

if(typeof(arguments[0]) == 'function'){
    console.log("Got a neglected argument");
    console.log(arguments[0]());
}

After executing f0(); (which prints 1 to screen), we return f1 which points to function returned by log('2'), this takes in log('4');

This can be verified by doing: world()()()

this outputs:

2
4
undefined
share|improve this answer
    
Thanks. any idea to fix this with this format?? –  Ken OKABE Jan 16 at 11:10

This is because you are just returning and returning everything...

There are three thing printed in the output:

1
 function ()
    {
      f0();
      return f1;
    }

2

1) first output: 1

this is because:console.log(args) is only executed once in your chaining, because f0 is executed only once at the last where it finds args to be 1(because of returning each nested function, what value you return at the last is a function f1 which executes f0() when the value of args is 1. then it prints 1 to the console.

2) second output the function f1

the return f1;(which is returned to the function when you passed args as 1) executed at the last returns

  function ()
            {
              f0();
              return f1;
            }

back to the variable world, hence only the inner nested function is printed to the console.

3) Third output: 2

then when you execute the functionworld(),

Again the function f1 is executed directly(see there is just a little difference between world and world()) but this time the returned function for when you passed args as 2.

Reason: world will output only the function, world() will execute the function.
When you write world(), at the last time where the function f1 is returned the value for args was 2 is directly executed.

I know I have terribly worded the answer.. but hope this helps (hope you understand)

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.