Fable: 应用以无点样式声明的函数失败

创建于 2017-10-22  ·  46评论  ·  资料来源: fable-compiler/Fable

描述

我正在尝试使用https://fsharpforfunandprofit.com/posts/elevated-world-3/ 中的以下代码
它在 Fable 中无法正常工作,但在 dotnet 核心中工作正常。

module AppFunc

open System

let (<!>) = Result.map


let apply fResult xResult = 
    match fResult,xResult with
    | Ok f, Ok x ->
        Ok (f x)
    | Error errs, Ok x ->
        Error errs
    | Ok f, Error errs ->   
        Error errs
    | Error errs1, Error errs2 ->
        // concat both lists of errors
        Error (List.concat [errs1; errs2])

let (<*>) = apply

我正在使用如下所示

let private createLoginInternal username password = 
    {UserName = username; Password = password }

let createLogin username password =
    createLoginInternal <!> (Email.create username) <*> (Password.create password)

类型电子邮件和密码被声明为

type Email = EmailType of string
with 
    static member create = function
        | x when String.IsNullOrEmpty(x) -> Error ["Email must not be empty."]
        | x when not (x.Contains("@")) -> Error ["Email must contain '@'."]
        | x when not (x.Contains(".")) -> Error ["Email must contain '.'."]
        | x -> Ok(EmailType x)

    member this.Value =
        let _val (EmailType x) =
            x

        _val this

type Password = PasswordType of string
with 
    static member create = function
        | x when String.IsNullOrEmpty(x) -> Error ["Password must not be empty."]
        | x when x.Length < 5 -> Error ["Password must have atleast 5 characters."]
        | x -> Ok(PasswordType x)

    member this.Value =
        let _val (PasswordType x) =
            x

        _val this

从 Suave 应用程序调用 create login 如下所示工作正常

createLogin "[email protected]" "masterkey"

但 Fable 失败,错误Unable to process a message: TypeError: matchValue[0].data is not a function

最有用的评论

使用Option在 REPL 中无需任何外部依赖即可重现:

open System

let apply f x =
  match f, x with
  | Some f, Some x -> Some (f x)
  | _ -> None

let (<!>) = Option.map
let (<*>) = apply

type Login =
  { Name : Name
    Password : Password }

and Name =
  | Name of string

  static member create = function
    | s when not (String.IsNullOrEmpty s) -> Some (Name s)
    | _ -> None

and Password =
  | Password of string

  static member create = function
    | s when String.length s >= 5 -> Some (Password s)
    | _ -> None

let private createLoginInternal name password =
  { Name = name; Password = password }

let createLogin name password =
  createLoginInternal <!> (Name.create name) <*> (Password.create password)

createLogin "jd" "secret"
|> printfn "%A"

但是,通过此更改可以轻松修复:

   | _ -> None

-let (<!>) = Option.map
+let (<!>) f x = Option.map f x
 let (<*>) = apply

 type Login =

@sandeepc24你能尝试这样做吗:

-let (<!>) = Result.map
+let (<!>) f x = Result.map f x

所有46条评论

使用Option在 REPL 中无需任何外部依赖即可重现:

open System

let apply f x =
  match f, x with
  | Some f, Some x -> Some (f x)
  | _ -> None

let (<!>) = Option.map
let (<*>) = apply

type Login =
  { Name : Name
    Password : Password }

and Name =
  | Name of string

  static member create = function
    | s when not (String.IsNullOrEmpty s) -> Some (Name s)
    | _ -> None

and Password =
  | Password of string

  static member create = function
    | s when String.length s >= 5 -> Some (Password s)
    | _ -> None

let private createLoginInternal name password =
  { Name = name; Password = password }

let createLogin name password =
  createLoginInternal <!> (Name.create name) <*> (Password.create password)

createLogin "jd" "secret"
|> printfn "%A"

但是,通过此更改可以轻松修复:

   | _ -> None

-let (<!>) = Option.map
+let (<!>) f x = Option.map f x
 let (<*>) = apply

 type Login =

@sandeepc24你能尝试这样做吗:

-let (<!>) = Result.map
+let (<!>) f x = Result.map f x

这个示例更紧凑:

let apply f x =
  match f, x with
  | Some f, Some x -> Some (f x)
  | _ -> None

let (<!>) f x = Option.map f x
let (<*>) = apply

let x = (+) <!> Some 40 <*> Some 2
x |> Option.iter (printfn "%i")

要使具有两个以上参数的函数起作用,我们还必须对<*>执行相同的操作,否则它会失败并出现相同的错误:

let (<!>) f x = Option.map f x
let (<*>) f x = apply f x

let add3 a b c = a + b + c
let x = add3 <!> Some 40 <*> Some 1 <*> Some 1 

-let () = 结果.map
+let () fx = 结果.map fx

这有效,谢谢 inosik

非常感谢@inosik 的帮助! 是的,无点风格有时会混淆寓言。 很高兴你让它在@sandeepc24工作,但我会尝试检查它是否可以被修复。 感谢报告:)

使用最新的开发版本let (<*>) = apply也可以工作,所以也许问题只是解决了 :smile: 虽然在使用点免费版本 ( let (<*>) f x = apply f x ) 时生成的 JS 代码要好得多,所以我d 建议使用那个:+1:

我暂时关闭这个问题,如果你发现类似的问题,请告诉我。

使用最新的开发版本 let (<*>) = apply 也可以使用

我只是做了一个完整的构建,然后是.\build.cmd QuickFableCompilerTest ,它不适用于无点<!><*> 。 我将此添加到QuickTest.fs

let apply f x =
    match f, x with
    | Some f, Some x -> Some (f x)
    | _ -> None

let add3 a b c = a + b + c

module PointFree =
    let (<!>) = Option.map
    let (<*>) = apply
    let x = add3 <!> Some 40 <*> Some 1 <*> Some 1

module Pointful =
    let (<!>) f x = Option.map f x
    let (<*>) f x = apply f x
    let x = add3 <!> Some 40 <*> Some 1 <*> Some 1

let ``My Test``() =
    equal PointFree.x Pointful.x

它仍然崩溃:

D:\projects\fable\src\tools\temp\QuickTest.js:1503
            return new Some($var1[1]($var1[2]));
                                    ^

TypeError: $var1[1] is not a function
    at apply (D:\projects\fable\src\tools\temp\QuickTest.js:1503:37)
    at D:\projects\fable\src\tools\temp\QuickTest.js:1521:20
    at curriedFn (D:\projects\fable\src\tools\temp\QuickTest.js:1466:25)
    at D:\projects\fable\src\tools\temp\QuickTest.js:1525:78
    at Object.<anonymous> (D:\projects\fable\src\tools\temp\QuickTest.js:1529:2)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)

嗯,你说得对。 这仍然是一个问题。 非常感谢您的新测试,我会尝试修复它! :+1:

@alfonsogarciacaro这是使用 3 个参数吗?
您计划何时发布此修复程序,因为我需要它继续我的开发?

我花了更多时间研究这个问题,但仍然收到此错误。 这是我的代码。 当我调用 createUser 时,我得到同样的错误。

module Domain.StringTypes

open System

let private applyValidations value okFun errorFun validations =
    let validationResults = validations
                            |> List.fold(fun a (f,m) -> if f value then m :: a else a) []
                            |> List.rev // Reverse the list as error messages are added as head element to the list.
    if List.isEmpty validationResults then
        okFun
    else
        errorFun validationResults

type String150 =
    String150Type of string
    with
        static member create = function
            | x when String.length(x) <= 150 -> Ok(String150Type x)
            | _ -> Error(["Length cannot be more than 150."])

        static member CreateNonBlank stringTitle = function
            | x when (String.length(x) > 0 && String.length(x) <= 150) -> Ok(String150Type x)
            | _ -> 
                let title = if String.IsNullOrEmpty stringTitle then "String" else stringTitle
                Error([title + " cannot be blank and must be less than 150."])

        member this.Value =
            let _val (String150Type x) =
                x

            _val this

type Email = EmailType of string
with 
    static member Create (title, email) =
        [ 
            (String.IsNullOrEmpty, title + " must not be empty.")
            ((fun x -> not (x.Contains("@"))), title + " must contain '@'.")
            ((fun x -> not (x.Contains("."))), title + " must contain '.'.")
        ] 
        |> applyValidations email (Ok (EmailType email)) Error

    static member Create email =
        Email.Create ("Email", email)

    member this.Value =
        let _val (EmailType x) =
            x

        _val this


type Password = PasswordType of string
with 
    static member Create (title, password) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            ((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations password (Ok (PasswordType password)) Error

    static member Create password =
        Password.Create ("Password", password)

    member this.Value =
        let _val (PasswordType x) =
            x

        _val this


type FirstName = FirstNameType of string
with 
    static member Create (title, firstName) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            //((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations firstName (Ok (FirstNameType firstName)) Error

    static member Create firstName =
        FirstName.Create ("FirstName", firstName)

    member this.Value =
        let _val (FirstNameType x) =
            x

        _val this


type LastName = LastNameType of string
with 
    static member Create (title, lastName) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            //((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations lastName (Ok (LastNameType lastName)) Error

    static member Create lastName =
        LastName.Create ("LastName", lastName)

    member this.Value =
        let _val (LastNameType x) =
            x

        _val this


type ContactName = ContactNameType of string
with 
    static member Create (title, contactName) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            //((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations contactName (Ok (ContactNameType contactName)) Error

    static member Create contactName =
        ContactName.Create ("ContactName", contactName)

    member this.Value =
        let _val (ContactNameType x) =
            x

        _val this


type Website = WebsiteType of string
with 
    static member Create (title, website) =
        match website with
        | Some website ->
            [
                // If website is not blank then it must start with http in it.
                ((fun (x : string) -> (x.Length > 0 && not (x.ToLower().StartsWith("http")))), title + " must have http.")
            ] 
            |> applyValidations website (Ok (Some (WebsiteType website))) Error
        | None -> Ok None

    static member Create website =
        Website.Create ("Website", website)

    member this.Value =
        let _val (WebsiteType x) =
            x

        _val this

type Phone = PhoneType of string
with 
    static member Create (title, phone) =
        match phone with
        | Some phone -> 
            [
                // If phone is not blank then it must have atleast 4 digits.
                ((fun (x : string) -> x.Length > 0 && x.Length < 5), title + " must have at least 4 digits.")
            ] 
            |> applyValidations phone (Ok (Some (PhoneType phone))) Error
        | None -> Ok None

    static member Create phone =
        Phone.Create ("Phone", phone)

    member this.Value =
        let _val (PhoneType x) =
            x

        _val this

type Mobile = MobileType of string
with 
    static member Create (title, mobile) =
        match mobile with
        | Some mobile ->
            [
                // If phone is not blank then it must have atleast 4 digits.
                ((fun (x : string) -> x.Length > 0 && x.Length < 5), title + " must have at least 4 digits.")
            ] 
            |> applyValidations mobile (Ok (Some (MobileType mobile))) Error
        | None -> Ok None

    static member Create mobile =
        Mobile.Create ("Mobile", mobile)

    member this.Value =
        let _val (MobileType x) =
            x

        _val this

type Fax = FaxType of string
with 
    static member Create (title, fax) =
        match fax with
        | Some fax ->
            [
                // If phone is not blank then it must have atleast 4 digits.
                ((fun (x : string) -> x.Length > 0 && x.Length < 5), title + " must have at least 4 digits.")
            ] 
            |> applyValidations fax (Ok (Some (FaxType fax))) Error
        | None -> Ok None

    static member Create fax =
        Fax.Create ("Fax", fax)

    member this.Value =
        let _val (FaxType x) =
            x

        _val this

`
Here is the UserDto declaration.

`
type UserDto = {
    FirstName : FirstName
    LastName : LastName
    ContactName : ContactName
    Email : Email
    Website : Website option
    Phone : Phone option
    Mobile : Mobile option
    Fax : Fax option
}

let private createUserInternal firstName lastName contactName email website phone mobile fax = 
    { 
        FirstName = firstName
        LastName = lastName
        ContactName = contactName
        Email = email
        Website = website
        Phone = phone
        Mobile = mobile
        Fax = fax
     }

let createUser firstName lastName contactName email website phone mobile fax = 
    createUserInternal <!> FirstName.Create firstName 
        <*> LastName.Create lastName 
        <*> ContactName.Create contactName 
        <*> Email.Create email
        <*> Website.Create website
        <*> Phone.Create phone 
        <*> Mobile.Create mobile 
        <*> Fax.Create fax

有关信息, @sandeepc24似乎没有更新到 Fable 的测试版,所以他没有在本地安装修复程序。 我们应该等待确认该错误仍然存​​在或不存在,但使用最新的 Fable 测试版。

马克西姆·曼格尔
@sandeepc24你升级到最新的寓言测试版了吗?
桑迪普·钱德拉
还没有,我该怎么做?

@sandeepc24抱歉,我想我在测试版中添加了修复程序,但这仍然存在一些其他问题。 让我在发布新版本之前修复它们。 之后,您只需在paket.dependencies添加prelease即可升级它,例如:

clitool dotnet-fable prerelease
nuget Fable.Core prerelease

然后运行mono .paket/paket.exe updatedotnet restore 。 或者,您能否@inosik示例中添加一个测试

@alfonsogarciacaro - 很抱歉打扰您,但我想知道这是否已解决?

我想知道同样的 ;) 你检查过最新版本吗?

我更新到 1.3,但问题仍然存在:-(

这是我的 paket.lock 文件的图像。

image

@sandeepc24您能否尝试在没有依赖关系的较小代码中隔离问题? 此注释中的代码很长,并且不是通过将其粘贴到文件中来编译的。 此外,如果未提供预期结果,则很难从中创建测试以确保该问题不会在未来版本中再次出现。

这是一个文件中的代码

#r "node_modules/fable-core/Fable.Core.dll"

open System
open System.Threading
open Fable.Core
open Fable.Import.Browser

let (<!>) = Result.map

let apply fResult xResult = 
    match fResult,xResult with
    | Ok f, Ok x ->
        Ok (f x)
    | Error errs, Ok x ->
        Error errs
    | Ok f, Error errs ->   
        Error errs
    | Error errs1, Error errs2 ->
        // concat both lists of errors
        Error (List.concat [errs1; errs2])

let (<*>) f x = apply f x

let private applyValidations value okFun errorFun validations =
    let validationResults = validations
                            |> List.fold(fun a (f,m) -> if f value then m :: a else a) []
                            |> List.rev // Reverse the list as error messages are added as head element to the list.
    if List.isEmpty validationResults then
        okFun
    else
        errorFun validationResults

type String150 =
    String150Type of string
    with
        static member create = function
            | x when String.length(x) <= 150 -> Ok(String150Type x)
            | _ -> Error(["Length cannot be more than 150."])

        static member CreateNonBlank stringTitle = function
            | x when (String.length(x) > 0 && String.length(x) <= 150) -> Ok(String150Type x)
            | _ -> 
                let title = if String.IsNullOrEmpty stringTitle then "String" else stringTitle
                Error([title + " cannot be blank and must be less than 150."])

        member this.Value =
            let _val (String150Type x) =
                x

            _val this

type Email = EmailType of string
with 
    static member Create (title, email) =
        [ 
            (String.IsNullOrEmpty, title + " must not be empty.")
            ((fun x -> not (x.Contains("@"))), title + " must contain '@'.")
            ((fun x -> not (x.Contains("."))), title + " must contain '.'.")
        ] 
        |> applyValidations email (Ok (EmailType email)) Error

    static member Create email =
        Email.Create ("Email", email)

    member this.Value =
        let _val (EmailType x) =
            x

        _val this


type Password = PasswordType of string
with 
    static member Create (title, password) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            ((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations password (Ok (PasswordType password)) Error

    static member Create password =
        Password.Create ("Password", password)

    member this.Value =
        let _val (PasswordType x) =
            x

        _val this


type FirstName = FirstNameType of string
with 
    static member Create (title, firstName) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            //((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations firstName (Ok (FirstNameType firstName)) Error

    static member Create firstName =
        FirstName.Create ("FirstName", firstName)

    member this.Value =
        let _val (FirstNameType x) =
            x

        _val this


type LastName = LastNameType of string
with 
    static member Create (title, lastName) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            //((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations lastName (Ok (LastNameType lastName)) Error

    static member Create lastName =
        LastName.Create ("LastName", lastName)

    member this.Value =
        let _val (LastNameType x) =
            x

        _val this


type ContactName = ContactNameType of string
with 
    static member Create (title, contactName) =
        [
            (String.IsNullOrEmpty, title + " must not be empty.")
            //((fun x -> x.Length < 5), title + " must have atleast 5 characters.")
        ] 
        |> applyValidations contactName (Ok (ContactNameType contactName)) Error

    static member Create contactName =
        ContactName.Create ("ContactName", contactName)

    member this.Value =
        let _val (ContactNameType x) =
            x

        _val this


type Website = WebsiteType of string
with 
    static member Create (title, website) =
        match website with
        | Some website ->
            [
                // If website is not blank then it must start with http in it.
                ((fun (x : string) -> (x.Length > 0 && not (x.ToLower().StartsWith("http")))), title + " must have http.")
            ] 
            |> applyValidations website (Ok (Some (WebsiteType website))) Error
        | None -> Ok None

    static member Create website =
        Website.Create ("Website", website)

    member this.Value =
        let _val (WebsiteType x) =
            x

        _val this

type Phone = PhoneType of string
with 
    static member Create (title, phone) =
        match phone with
        | Some phone -> 
            [
                // If phone is not blank then it must have atleast 4 digits.
                ((fun (x : string) -> x.Length > 0 && x.Length < 5), title + " must have at least 4 digits.")
            ] 
            |> applyValidations phone (Ok (Some (PhoneType phone))) Error
        | None -> Ok None

    static member Create phone =
        Phone.Create ("Phone", phone)

    member this.Value =
        let _val (PhoneType x) =
            x

        _val this

type Mobile = MobileType of string
with 
    static member Create (title, mobile) =
        match mobile with
        | Some mobile ->
            [
                // If phone is not blank then it must have atleast 4 digits.
                ((fun (x : string) -> x.Length > 0 && x.Length < 5), title + " must have at least 4 digits.")
            ] 
            |> applyValidations mobile (Ok (Some (MobileType mobile))) Error
        | None -> Ok None

    static member Create mobile =
        Mobile.Create ("Mobile", mobile)

    member this.Value =
        let _val (MobileType x) =
            x

        _val this

type Fax = FaxType of string
with 
    static member Create (title, fax) =
        match fax with
        | Some fax ->
            [
                // If phone is not blank then it must have atleast 4 digits.
                ((fun (x : string) -> x.Length > 0 && x.Length < 5), title + " must have at least 4 digits.")
            ] 
            |> applyValidations fax (Ok (Some (FaxType fax))) Error
        | None -> Ok None

    static member Create fax =
        Fax.Create ("Fax", fax)

    member this.Value =
        let _val (FaxType x) =
            x

        _val this

type UserDto = {
    FirstName : FirstName
    LastName : LastName
    ContactName : ContactName
    Email : Email
    Website : Website option
    Phone : Phone option
    Mobile : Mobile option
    Fax : Fax option
}

let private createUserInternal firstName lastName contactName email website phone mobile fax = 
    { 
        FirstName = firstName
        LastName = lastName
        ContactName = contactName
        Email = email
        Website = website
        Phone = phone
        Mobile = mobile
        Fax = fax
     }

let createUser firstName lastName contactName email website phone mobile fax = 
    createUserInternal <!> FirstName.Create firstName 
        <*> LastName.Create lastName 
        <*> ContactName.Create contactName 
        <*> Email.Create email
        <*> Website.Create website
        <*> Phone.Create phone 
        <*> Mobile.Create mobile 
        <*> Fax.Create fax


createUser "Sandeep" "Chandra" "Sandeep" "[email protected]" None None None None

输出应该是

 Ok {FirstName = FirstNameType "Sandeep";
      LastName = LastNameType "Chandra";
      ContactName = ContactNameType "Sandeep";
      Email = EmailType "[email protected]";
      Website = None;
      Phone = None;
      Mobile = None;
      Fax = None;}

我无法使用 Fable 1.3.0 重现它,使用 Webpack 和 Rollup 作为捆绑器,并编译为 Node 应用程序。 它也适用于 REPL。

我还尝试将 apply 运算符更改为无点样式,这也有效。

@sandeepc24您的 JS 依赖项是最新的吗?

<!>运算符更改为有针对性的样式 ( let (<!>) f x = Result.map f x ) 使其中断。

再次简化为:

let apply f x =
    match f, x with
    | Some f, Some x -> Some (f x)
    | _ -> None

let (<!>) mapping option = Option.map mapping option
//let (<!>) = Option.map
let (<*>) f x = apply f x
//let (<*>) = apply

let add3 a b c = a + b + c

let sum = add3 <!> Some 1 <*> Some 2 <*> Some 3
printfn "%A" sum

| <!> | <*> | 结果 |
|------------|------------|--------|
| 点免费 | 点免费 | 好的 |
| 有意义的| 点免费 | 错误 |
| 点免费 | 有意义的| 好的 |
| 有意义的| 有意义的| 好的 |

这就是我们的测试通过的原因。 因为我们要么使用 point free 要么使用 pointful 风格,但没有使用任何排列。

参数的数量会改变结果 :confused:

let add a b = a + b
let add3 a b c = a + b + c

let apply f x =
    match f, x with
    | Some f, Some x -> Some (f x)
    | _ -> None

[<CompiledName("mapOp")>]
let (<!>) mapping option = Option.map mapping option
//let (<!>) = Option.map

[<CompiledName("applyOp")>]
let (<*>) f x = apply f x
//let (<*>) = apply

try
    let (Some x) = add <!> Some 1 <*> Some 2
    printfn "add: OK (%i)" x
with
| _ -> printfn "add: Error"

try
    let (Some x) = add3 <!> Some 1 <*> Some 2 <*> Some 3
    printfn "add3: OK (%i)" x
with
| _ -> printfn "add3: Error"

| <!> | <*> | 结果add | 结果add3 |
|------------|------------|--------------|-------- -------|
| 点免费 | 点免费 | 错误 | 好的 |
| 有意义的| 点免费 | 好的 | 错误 |
| 点免费 | 有意义的| 错误 | 好的 |
| 有意义的| 有意义的| 好的 | 好的 |

非常感谢@inosik 为调查问题所做的所有工作! 非常有趣的结果,我会尝试修复它......或者绝对禁止寓言中的点自由风格;)

寓言中禁止点自由风格

那么我们应该怎么做["foo"] |> List.exists ((=) "bar")呢? :微笑:

在无点情况下,生成的代码似乎错过了一些partialApply调用。 我将第一种情况(点免费地图和应用)更改为:

try
    let add1 = add <!> Some 1
    console.log ("add1", add1)

    let (Some x) = add1 <*> Some 2
    console.log ("add: OK (", x, ")")
with
| _ -> console.log "add: Error"

输出是这样的:

add1 0
add: Error

虽然它实际上应该是这样的:

add1 (...args) => {
        // _this = _this || this;
        const actualArgsLength = Math.max(args.length, 1);
        expectedArgsLength = Math.max(expectedArgsLength || f.length, 1);
        if (actualArgsLength >= expectedArgsLength) {
            const restArgs = args.splice(expectedArgsLength);
            const res = f(...args);
            if (typeof res === "function") {
                const newLambda = CurriedLambda(res);
                return restArgs.length === 0 ? newLambda : newLambda(...restArgs);
            }
            else {
                return res;
            }
        }
        else {
            return CurriedLambda((...args2) => {
                return f(...args.concat(args2));
            }, expectedArgsLength - actualArgsLength);
        }
    }
add: OK ( 3 )

抱歉,我的意思是函数声明中的无点样式。 在我们进一步调查之前,我添加了一条警告,建议用户明确声明所有参数。 我希望这暂时没问题:)

screen shot 2017-11-21 at 11 41 32 am

我已更新到最新版本的寓言 (1.3.2),但仍然存在此问题。

这是正常的点自由样式尚不支持。

在 1.3.1 中我们添加了一个警告,但是在不同的项目中它被提出太多,所以我们在 1.3.2 中删除了它。

请明确声明您的所有参数,它应该可以工作。

我不确定如何为我提供的测试用例执行此操作,您能否举一个用于 createUserInternal 的示例?

@sandeepc24将您的运算符更改为有针对性的样式,例如: let (<!>) f x = Result.map f x

我们能否检测到用户简单地给函数“别名”的情况? 我认为在这种情况下不需要CurriedLambda包装,对吗?

@inosik我认为可以检测以无点样式声明的成员并将它们转换为正常方法。 但主要问题是调用站点,其中 F# 编译器提供的 AST 在一种情况下非常不同。 检查以下示例的 F#(不是 Fable)AST 在无点(左)和有点(右)样式之间的差异。 不幸的是,我试图检测这些情况并解决所有情况(如部分应用等)都没有成功😞

screen shot 2017-11-25 at 2 13 30 pm

我正在努力将警告限制在有问题的情况下,我想我几乎已经做到了。 这可能是目前唯一的解决方案。

@inosik我已经尝试过了,但是对于我有超过 3 个参数的情况,它也失败了。

@alfonsogarciacaro该屏幕截图显示了呼叫站点,我的意思是声明:

let add a b = a + b
let sumOf = add

我们可以检测到sumOfadd的别名吗? 然后我们可以生成这样的东西:

function add(a, b) { return a + b; }
var sumOf = add;

另外, @sandeepc24是对的,即使是有针对性的风格也不适用于所有情况。 以下代码段因“thenAdd2 不是函数”而失败:

let apply f x =
  match f, x with
  | Some f, Some x -> Some (f x)
  | _ -> None

[<CompiledName("op_map")>]
let (<!>) f x = Option.map f x

[<CompiledName("op_apply")>]
let (<*>) f x = apply f x

let add a b c d = a+b+c+d

try
  let add1 = add <!> Some 1
  let thenAdd2 = add1 <*> Some 2
  let thenAdd3 = thenAdd2 <*> Some 3
  let sum = thenAdd3 <*> Some 4
  printfn "%A" sum
with
| e -> printfn "%s" e.Message

也许 JS 根本无法将函数作为一等值使用? :微笑:

@inosik是的,我们可以这样做,但我不确定它会解决所有情况,因为您仍然可以使用无点样式(例如let add5 = add 5 )声明非别名函数。 但确切地说,显示调用站点的重点是更改声明还不够,我们需要跟踪对该函数的所有调用,并相应地更改 AST,但我没有成功地做到这一点。 请注意,在此 REPL 示例的 JS 代码中,F# 编译器首先不带参数调用op_Dollar ,然后一个一个地应用实际参数,这在许多情况下与 Fable 的非柯里化优化相冲突。

我已经在 FCS repo 中打开了一个关于此的问题,让我们看看他们是否可以对此有所了解: https :

啊,好的,现在我明白了。

但是,我认为这里的真正问题实际上并不是点自由样式,因为我上面评论中的代码也不起作用,而以下代码运行得非常好:

let add a b c d = a + b + c + d

let apply f x = f x

let ($) = apply

try
    let sum = add $ 1 $ 2 $ 3 $ 4
    console.log ("OK:", sum)
with
| e -> console.log ("Error:", e.Message)

如果函数被包装在另一种类型中,部分应用程序似乎有些损坏: @sandeepc24的原始片段中的Result再现样本中的Option 。 我也可以用自定义的Maybe<'a>类型重现它。

@inosik啊,好吧,我错过了。 感谢您指出:+1:这实际上是一个不同的问题(用Some包装函数)它应该通过这个提交来修复。

好的,这使它适用于Option :+1: 但这不适用于其他类型,这仍然可以使用自定义类型( type Maybe<'a> = Just of 'a | Nothing )和可能的Result重现

@inosik结果也应该有效,因为根据@alfonsogarciacaro这修复了我对这个项目的失败测试: https :

@MangelMaxime当参数超过 2 个时,这似乎会中断:

| add函数 | 呼叫站点 | 结果 |
|---|---|---|
| let add a b = a+b | let sum = add <!> Ok 1 <*> Ok 2 | Ok 3 |
| let add a b c = a+b+c | let sum = add <!> Ok 1 <*> Ok 2 <*> Ok 3 | 错误 |

使它中断的参数数量对我来说看起来很随机,因为它已经使用了Option 3 个参数。 我还不认识这里的模式 :smile:

为了完整起见,这是我添加到QuickTest.fs

let apply (f : Result<_, unit>) (x : Result<_, unit>) =
    match f, x with
    | Ok f, Ok x -> Ok (f x)
    | _ -> Result.Error ()

let (<!>) f x = Result.map f x
let (<*>) f x = apply f x

let add a b c = a+b+c

[<Test>]
let Test () =
    let sum = add <!> Ok 1 <*> Ok 2 <*> Ok 3
    equal (Ok 6) sum

Test ()

好吧,这太疯狂了。 当我将上面代码中的运算符更改为指向自由样式时,它可以工作。 6 个参数和计数。 :厌倦:

@inosik ...这变成了寓言中的测试 1400! 好的,我再次尝试修复应用程序,我希望这次需要更长的时间,直到您发现另一个失败的测试:wink:

谢谢@alfonsogarciacaro ,我可以通过下载 Fable 的测试版来测试它吗?

我更新到 Fable 1.3.3,但上面的 createUser 函数仍然存在问题。

@sandeepc24该修复程序尚未发布。 但是我认为在 Fable 1.3.3 中,如果您将两个运算符更改为指向自由样式( let (<!>) = Result.map ),它应该可以工作。 如果没有,您将不得不等待下一个版本或自己构建 Fable。

@sandeepc24 Fable 1.3.4 已经发布,希望这最终解决了这个问题:)

我已经对此进行了测试,现在对我有用。 非常感谢你们,非常感谢。

太棒了,非常感谢您的确认!

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

krauthaufen picture krauthaufen  ·  3评论

MangelMaxime picture MangelMaxime  ·  3评论

jwosty picture jwosty  ·  3评论

forki picture forki  ·  3评论

forki picture forki  ·  3评论