question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Partially applied function as a record field is incorrectly curried when the first argument is unit

See original GitHub issue

(boy, that title is a mouthful)

Description

Another one of those weird partial application bugs, here we go!

Consider the following snippet that works just fine:

let fn (x: unit) (y: string) = y.Length

let result = fn () "hello"
printfn "result = %d" result // result = 5

let partiallyApplied = fn ()  
let secondResult = partiallyApplied "hello" 
printfn "secondResult = %d" secondResult // secondResult = 5

is compiled to the following (using REPL):

export function fn(x, y) {
  return y.length | 0;
}

export const result = fn(null, "hello");
toConsole(printf("result = %d"))(result);
export const partiallyApplied = CurriedLambda(function (y) {
  return fn(null, y);
});
export const secondResult = partiallyApplied("hello");
toConsole(printf("secondResult = %d"))(secondResult);

Carefully notice the part where the partiallyApplied function is defined, it correctly passes null where unit is expected:

export const partiallyApplied = CurriedLambda(function (y) {
  return fn(null, y);
});

So far so good, nothing to worry about. Now put that same function inside a record:

type Rec = { fn : unit -> string -> int  }
let fn (x: unit) (y: string) = y.Length
let record = { fn = fn }

let recordResult = record.fn () "hello"
printfn "recordResult = %d" recordResult // recordResult = 5

let recordPartiallyApplied = record.fn ()  

// HERE COMES THE ERROR: TypeError: Cannot read property 'length' of undefined
let recordSecondResult = recordPartiallyApplied "hello" 
printfn "recordSecondResult = %d" recordSecondResult 

This is because the recordPartiallyApplied function is defined incorrectly:

Actuall

export const recordPartiallyApplied = CurriedLambda(CurriedLambda(record.fn)());

where I would expect that it is defined the same way without a record:

Expected

export const recordPartiallyApplied = CurriedLambda(CurriedLambda(record.fn)(null));

// OR 

export const recordPartiallyApplied = CurriedLambda(function (y) {
  return record.fn(null, y);
});

// OR 

export const recordPartiallyApplied = function (y) {
  return record.fn(null, y);
};

Notice that this will work fine if the argument was not unit, if it was int the example works just fine and it also works when unit is the second argument (hence, the title) where the first string parameter is passed correctly as well

The full compiled JS code from the snippet with records

import { setType } from "fable-core/Symbol";
import _Symbol from "fable-core/Symbol";
import { Unit, Function as _Function } from "fable-core/Util";
import { printf, toConsole } from "fable-core/String";
import CurriedLambda from "fable-core/CurriedLambda";
export class Rec {
  constructor(fn) {
    this.fn = fn;
  }

  [_Symbol.reflection]() {
    return {
      type: "Test.Rec",
      interfaces: ["FSharpRecord"],
      properties: {
        fn: _Function([Unit, "string", "number"])
      }
    };
  }

}
setType("Test.Rec", Rec);
export function fn(x, y) {
  return y.length | 0;
}
export const record = new Rec(function (x, y) {
  return fn(null, y);
});
export const recordResult = record.fn(null, "hello");
toConsole(printf("recordResult = %d"))(recordResult);
export const recordPartiallyApplied = CurriedLambda(CurriedLambda(record.fn)());
export const recordSecondResult = recordPartiallyApplied("hello");
toConsole(printf("recordSecondResult = %d"))(recordSecondResult);

Related information

  • Fable version (dotnet fable --version): 1.3.10 and REPL
  • Operating system: Ubuntu 17.0

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:3
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
Zaid-Ajajcommented, Apr 18, 2018

Awesome, you are fast ❤️

A bit of side note, since there is a lot of refactoring going on, it would be nice to add some comments / docs to the compiler functions in this early stage, especially for modules from the Transform directory. I tried to go through the code to understand where this problem could be originating from but it is a bit hopeless (for me at least) to understand what exactly is going on from just pattern matching.

0reactions
Zaid-Ajajcommented, Apr 22, 2018

I agree that adding docs to existing code might too much of a challenge for now, it is however very much appreciated for newly created functions, patterns etc.

An idea that just popped in my mind is that (maybe at a later stage) you could send a PR with questions in the code and I reply them in the appropriate places so the answers remain as comments.

Sounds like a good idea, actually issues would work too so we can always reference back to the conversation for that time. Another idea that I got is to use more active patterns with very descriptive names for specific patterns that are not so obvious by just looking at them

Read more comments on GitHub >

github_iconTop Results From Across the Web

Scala currying vs partially applied functions
When partially applied, the costs of representing a curried and non-curried version in memory might be different, so the runtime performance ...
Read more >
Currying, Partial Application, and Infinite Application
In curriedAdd we pass the value for a first, and get a function back that accepts b , then evaluates the result against...
Read more >
Partial application
Intuitively, partial function application says "if you fix the first arguments of the function, you get a function of the remaining arguments".
Read more >
Currying vs partial function application | Jon Skeet's coding blog
Whereas partial function application converts a function with N parameters into a function with N-1 parameters by applying one argument, ...
Read more >
Operator based currying and partial application
curry is operator based and a partially applied function retains named arguments for easier autocomplete etc. curry provides three mechanisms ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found