19

Note: The point of this question is more from a curiosity perspective. I want to know out of curiosity whether it is even possible to transliterate the Haskell implementation into a functional C# equivalent.

So I've been learning myself Haskell for great good, and while solving Project Euler problems I ran into this beautiful Haskell Fibonacci implementation:

fibs :: [Integer]
fibs = 1:1:zipWith (+) fibs (tail fibs)

Of course I was tempted to write a C# version like this, so:

  1. If I do this:

    IEnumerable<int> fibs =
        Enumerable.Zip(Enumerable.Concat(new int[] { 1, 1 }, fibs),
                                                           //^^error
                                              fibs.Skip(1), (f, s) => f + s);
    

    The error says use of unassigned local variable fibs.

  2. So I went slightly imperative, while this compiles...

    public static IEnumerable<int> Get()
    {
        return Enumerable.Zip(Enumerable.Concat(new int[] { 1, 1 }, Get()),
                                              Get().Skip(1), (f, s) => f + s);
    }
    

    It breaks with a stack overflow exception! So I came here..

Questions:

  1. Can anyone think of a functional C# equivalent that works?
  2. I'd like some insight into why my solutions don't work.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gideon
  • 19,329
  • 11
  • 72
  • 113
  • 1
    I am not sure about language semantics of C#, but haskell solution works because of laziness. It only computes what is necessary. Is that the case in c# too? – Satvik Oct 01 '13 at 06:12
  • Take a look at http://stackoverflow.com/questions/2062540/help-with-linq-expression – Offler Oct 01 '13 at 06:14
  • @satvik only in special cases. There is a class called "Lazy" to have laziness, also some things like linq are lazy. Also keywords like "yield" can be used, look at the accepted answer in http://stackoverflow.com/questions/4564472/how-can-i-return-a-fibonacci-series-should-i-use-ilist (with yield you don't reach the code until it is needed) – Offler Oct 01 '13 at 06:17
  • Yep. Enumberables are lazy. I can write an `IEnumberable` of an infinite list of numbers. And do `infiniteEnumerable.Take(10)`, just like haskell this works in C#. – gideon Oct 01 '13 at 06:24
  • You might have an easier time translating Haskell code into [F#](http://www.tryfsharp.org/), as they're both functional, declarative programming languages. – mcandre Oct 02 '13 at 00:35

6 Answers6

18

The answer to your first question is: this is how to do it in C#:

using System;
using System.Collections.Generic;
using System.Linq;

class P
{
  static IEnumerable<int> F()
  {
    yield return 1;
    yield return 1;
    foreach(int i in F().Zip(F().Skip(1), (a,b)=>a+b))
      yield return i;
  }

  static void Main()
  {
    foreach(int i in F().Take(10))
      Console.WriteLine(i);
  }
}

The answer to your second question is: C# is eager by default, so your method has an unbounded recursion. Iterators that use yield however return an enumerator immediately, but do not construct each element until required; they are lazy. In Haskell everything is lazy automatically.

UPDATE: Commenter Yitz points out correctly that this is inefficient because, unlike Haskell, C# does not automatically memoize the results. It's not immediately clear to me how to fix it while keeping this bizarre recursive algorithm intact.

Of course you would never actually write fib like this in C# when it is so much easier to simply:

static IEnumerable<BigInteger> Fib()
{
    BigInteger prev = 0;
    BigInteger curr = 1;
    while (true)
    {
        yield return curr;
        var next = curr + prev;
        prev = curr;
        curr = next;
    }
}
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thank you kind sir! I was hoping you would chance upon this. I'm having a major face palm moment with the double `yield return 1;` staring in my face. I'm been writing C# for so long, but thinking functionally is just such a different ball game. – gideon Oct 01 '13 at 06:30
  • 4
    Hmm. Unlike the Haskell version, this solution does not appear to cache elements of the sequence that have already been computed. So for each fibonacci number, it starts all the way at the beginning, and then yet again for each fibonacci it needs along the way. That explodes into exponential complexity. So it seems like this will be useless beyond the first few fibonacci numbers. Is there any way to fix that? – Yitz Oct 01 '13 at 09:19
  • Why did you import `System.Linq`? You only seem to be using generics and iterators here; I don't see any Linq queries. – Yitz Oct 01 '13 at 09:35
  • 2
    @Yitz - `Zip`, `Skip` and `Take` are all linq extension methods. – Lee Oct 01 '13 at 10:28
  • @Yitz you're right. how did you confirm that it doesn't cache (I'm curious) ? – gideon Oct 01 '13 at 11:03
  • 1
    @Yits - exactly. @gideon - compare with the F# version in my answer. Run `Seq.toArray (Seq.take 40 fibs)` with and without `Seq.cache` and observe the difference.. – t0yv0 Oct 01 '13 at 12:14
  • @Yitz: You are correct; I had forgotten that Haskell automatically memoizes as it goes. It's not immediately clear to me how to fix that. Of course, the right way to fix it is to not use this silly recursion in the first place. There are only a tiny number of fib numbers that fit into a 32 bit integer; the best solution is to put them in an array and then iterate over that! – Eric Lippert Oct 01 '13 at 15:04
  • The Haskell version uses Haskell's unlimited precision `Integer` type by default, not a 32-bit type. Not that you can compute very many fibonacci numbers in practice even with that... Anyway, I think the point of @gideon's exercise is to try to capture the flavor of the Haskell algorithm in C#. – Yitz Oct 01 '13 at 15:24
  • 1
    Thank you @EricLippert yep, I know the whole recursion thing isn't the right way to go about the problem in C#, I just wanted to know out of curiosity if I could _transliterate_ the code. _Update_: Precisely @Yitz! – gideon Oct 01 '13 at 15:25
  • 1
    Eric - nice thinking on using an array (although BigInteger goes much higher). One question, why not just set prev = 0 and eliminate the need for the extra yield return? Perhaps I am missing something. – Justin Oct 07 '13 at 05:58
12

Unlike the C# version provided in Eric Lippert's answer, this F# version avoids repeated computation of elements and therefore has comparable efficiency with Haskell:

let rec fibs =
    seq {
        yield 1
        yield 1
        for (a, b) in Seq.zip fibs (Seq.skip 1 fibs) do
            yield a + b
    }
    |> Seq.cache // this is critical for O(N)!
t0yv0
  • 4,714
  • 19
  • 36
  • 4
    Haskell's `zipWith` is basically F#'s `map2`, so the for loop can be replaced with: `yield! Seq.map2 (+) fibs (Seq.skip 1 fibs)` – Tarmil Oct 01 '13 at 13:40
  • 1
    Not surprising that it's easier in F#. But there should be some way to do this in C# too. – Yitz Oct 01 '13 at 15:05
  • 2
    @Yitz one can use `Seq.cache` from C#, it is in `FSharp.Core.dll`. May be a bit of a pain to implement correctly yourself. – t0yv0 Oct 01 '13 at 15:27
11

I have to warn you that I'm trying to fix your attempts, not to make a productive code. Also, this solution is good to make our brains to explode, and maybe the computer also.

In your first snippet you tried to call recursive your field or local variable, that is not possible.Instead we can try with a lambda which could be more similar to that. We know from Church, that is also not possible, at least in the traditional way. Lambda expressions are unnamed; you can't call them by their name ( inside of the implementation ). But you can use the fixed point to do recursion. If you have a sane mind there is big chance of not knowing what is that, anyway you should give a try to this link before continuing with this.

fix :: (a -> a) -> a
fix f = f (fix f)

This will be the c# implementation (which is wrong)

A fix<A>(Func<A,A> f) {
    return f(fix(f));
}

Why is wrong? Because fix(f) represents a beautiful stackoverflow. So we need to make it lazy:

A fix<A>(Func<Func<A>,A> f) {
    return f(()=>fix(f));
}

Now is lazy! Actually you will see a lot of this in the following code.

In your second snippet and also in the first, you have the problem that the second argument to Enumerable.Concat is not lazy, and you will have stackoverflow exception, or infinite loop in the idealistic way. So let's make it lazy.

static IEnumerable<T> Concat<T>(IEnumerable<T> xs,Func<IEnumerable<T>> f) {
   foreach (var x in xs)
     yield return x;
   foreach (var y in f())
     yield return y;
}

Now, we have the whole "framework" to implement what you have tried in the functional way.

void play() {
    Func<Func<Func<IEnumerable<int>>>, Func<IEnumerable<int>>> F = fibs => () => 
            Concat(new int[] { 1, 1 }, 
                    ()=> Enumerable.Zip (fibs()(), fibs()().Skip(1), (a,b)=> a + b));

    //let's see some action
    var n5      = fix(F)().Take(5).ToArray();       // instant 
    var n20     = fix(F)().Take(20).ToArray();      // relative fast
    var n30     = fix(F)().Take(30).ToArray();      //this will take a lot of time to compute
    //var n40   = fix(F)().Take(40).ToArray();      //!!! OutOfMemoryException 
}

I know that the F signature is ugly like hell, but this is why languages like haskell exists, and even F#. C# is not made for this work to be done like this. Now, the question is, why haskell can achieve something like this?Why? because whenever you say something like

a:: Int
a = 4

The most similar translation in C# is :

Func<Int> a = () => 4

Actually is much more involved in the haskell implementation, but this is the idea why similar method of solving problems looks so different if you want to write it in both languages

Bogdan Manole
  • 374
  • 1
  • 7
  • 2
    Oh, guy, you are almost awesome. Thanks for the answer – Viktor Lova Oct 04 '13 at 22:26
  • 1
    @Bogdan thanks very much for this answer. It's extremely insightful and to be honest I've had to keep rereading it and reading more about haskell's implementation and the link you added. – gideon Oct 11 '13 at 05:04
  • One should also point out that the Haskell version is actually `fix f = let a = f a in a` rather than `fix f = f (fix f)` as the former can avoid a space leak when `f` uses its argument structurally, that the latter must always suffer. – Edward Kmett Mar 24 '14 at 15:35
  • Nice to know, Edward , at the moment i wrote the comment, f (fix f) was the implementation, it's good that you pointed out the change in the base package. – Bogdan Manole Sep 08 '14 at 14:33
6

Here it is for Java, dependent on Functional Java:

final Stream<Integer> fibs = new F2<Integer, Integer, Stream<Integer>>() {
  public Stream<Integer> f(final Integer a, final Integer b) {
    return cons(a, curry().f(b).lazy().f(a + b));
  }
}.f(1, 2);

For C#, you could depend on XSharpX

Tony Morris
  • 3,045
  • 2
  • 29
  • 17
2

A take on Eric's answer that has Haskell equivalent performance, but still has other issues(thread safety, no way to free memory).

   private static List<int> fibs = new List<int>(){1,1};
   static IEnumerable<int> F()
   {
     foreach (var fib in fibs)
     {
      yield return fib;
     }
     int a, b;
     while (true)
     {
      a = fibs.Last();
      b = fibs[fibs.Count() - 2];
      fibs.Add(a+b);
      yield return a + b;
     }
   }
stonemetal
  • 6,111
  • 23
  • 25
  • Unfortunately this is not recursive so the only gain you have with memoizing the results is for multiple external calls to F. Also a small nitpick: fibs is a list so use .Count property and not the .Count() IEnumerable extension method. The latter should iterate the sequence each time it's called (yet it might be optimized out in the implementation but you should not rely on that) – Adrian Zanescu Dec 17 '13 at 16:09
0

Translating from a Haskell environment to a .NET environment is much easier if you use F#, Microsoft's functional declarative language similar to Haskell.

mcandre
  • 22,868
  • 20
  • 88
  • 147