Fable: 帮助寓言 2 开发

创建于 2018-03-21  ·  37评论  ·  资料来源: fable-compiler/Fable

不确定dev2.0分支是否已经处于可以开始寻求帮助的状态,但无论如何我都会这样做 :wink: 我在该分支中发生的主要变化下面列出,如何您能否快速测试一下,以及第一个 alpha 版本有哪些待处理的任务。 如果您有兴趣帮助他们中的任何一个,请在下面发表评论,以便我可以提供更多细节以共同努力💪

不完整的主要变化清单

  • Fable AST 现在包含 F# 中一些最常见的构造(列表、选项...),因此在必要时更容易更改它们在 JS 中的表示。 此外,AST 现在保留来自 F# 编译器的类型信息。
  • 记录和联合不会编译为 JS _classes_,而是分别编译为普通对象和数组。
  • 所有类型方法现在都编译为 JS 模块函数,以更好地与 tree Shaking 兼容(希望它也能帮助 JS 引擎优化代码)。 唯一的例外是附加到原型的覆盖方法(ToString、Equals)。 此外,嵌套的模块函数被编译为文件根模块的函数,名称被修改。
  • 当将实例转换为接口时,它将用包含接口函数的对象包装,这可以防止接口之间的名称冲突(这也是必要的,因为方法不再附加到原型)。
  • fable-core 中的一些模块(如 List 或 Array)现在是用 F# 编写的
  • 应用于 Fable AST 的优化现在是发生在FableTransforms隔离传递。 这应该使代码比寓言 1 更易于维护,其中优化通常应用于多个地方(特别是非柯里化优化,希望它在寓言 2 中工作得更好)。 然而,一些优化仍然需要在最后一次 Fable2Babel 传递中完成:尾调用和决策树(模式匹配)。

试一试

  • 如果您还没有克隆存储库
  • git checkout dev2.0
  • cd src/tools
  • bash quicktest.sh --build-core

这将编译并执行同一文件夹中的QuickTest.fs文件。 随意将其用作测试寓言 2 功能的游乐场(但请不要将其包含在您的 PR 中)。 第二次,如果src/js/fable-core文件没有改变你可以运行bash quicktest.sh (如果src/dotnet/Fable.Compiler文件没有改变你也可以运行bash quicktest.sh --no-build但是无论如何,使用 2.1.300-preview1 构建一个没有更改的项目应该很快)。

在 Windows 上,您应该能够使用 Git bash 运行脚本。

待处理的任务

  • 代码中散布着许多TODO!!!注释,我可能应该自己处理这些注释。

  • 由于插件不会包含在第一个 alpha 版本中,测试现在使用 Expecto API ,这更容易与 JS 测试运行器共享。 我已经转换了一些文件(ArithmeticTests、ArrayTests、ListTests),但其中大多数仍在等待一些爱。 我正在使用 Regex 将方法的签名更改为testCase但为它编写脚本可能是个好主意。 主要挑战之一是,鉴于测试现在是列表中的值,出现在文件中间的额外函数必须移至顶部,这在某些情况下可能会影响其他值。

  • fable-core 中的 Set 和 Map 模块必须移植到 F#。 我部分是为 Set 做的(Map 也可以从FunScript repo 中获取,我最初是从那里窃取灵感的)。 我们需要确保它们被正确编译为 JS,公共函数名称正确公开,检查我们是否需要从以前的 .ts 文件(保存在src/tools/old )中移植方法以及测试是否正常工作.

  • 因为 AST 变化太大,大部分 Replacements 模块都需要重写。 我已经做了大约 1000 行,还有一千行。 这是一个有点乏味的任务,但如果有人疯狂到可以接受它,请告诉我。 也许最快的方法是让屏幕共享会话向您展示需要完成的工作,然后拆分剩余的代码,以便我们可以更快地完成。

不会包含在第一个 alpha 版本中的东西:bigint、反射、插件。

我希望现在有足够的信息,非常感谢您的帮助!

dev2.0 help wanted

最有用的评论

仅供参考,如果有人正在移植测试文件,则无需移植SetTests.fsMapTests.fs我将这样做来测试模块集成。

此外,如果您使用 VSCode,您可以使用多项选择在一个文件中一次性完成所有工作。 在这里编写脚本似乎有点过于工程化:)。

2018-04-18 15 14 27

所有37条评论

@alfonsogarciacaro我想我会在寓言核心任务中使用Set以便我可以从这里获得灵感:)?

提醒一下,我们可以从许多回购中窃取灵感,

  1. visualfsharp 的官方核心库,它的地图实现看起来很像 FunScript 的:
    https://github.com/Microsoft/visualfsharp/blob/master/src/fsharp/FSharp.Core/map.fs
  2. OCaml/BucleScript 的,F# 和 OCaml 的核心语法没有太多区别,我认为我们可以轻松地将它们移植到 F#。
    https://github.com/BuckleScript/bucklescript/blob/master/jscomp/stdlib/map.ml

谢谢,@zaaack。 是的,FunScript 文件最初取自 FSharp.Core。 不确定它是否已经过时,但我倾向于更喜欢它,因为它已经在工作(寓言 1 中的Map.ts最初也是它的翻译)而且我认为它已经砍掉了一些我们没有的东西在JS中需要。 关于 Bucklescript,我不确定模块是 OCaml 中的值这一事实是否有问题,F# 中的 Map 也必须符合某些接口,而我花费最多的时间是使具有自定义比较的类型作为键正常工作,这我想它在 OCaml 中也有些不同。

顺便说一句,Bigint 确实取自 FSharp.Core,但我认为我们会在以后的版本中移植。

谢谢@zaaack我会在端口上工作时记住这一点。

嗨,阿方索,我想在期待测试部分提供帮助

太好了@EdouardM ,谢谢! 如果您可以编写一个脚本来自动进行大部分转换,那就太好了。 如果你愿意,我们可以有一个简短的屏幕共享会议来展示需要做的事情:)

当然,我需要一个短暂的开球才能走上正轨。

从巴黎-马德里时间下午 6:30 开始,您今晚有空吗?
还是周日?

如果你是,请给我发送一个Skype电话邀请。

谢谢
爱德华

勒文。 2018 年 3 月 23 日 à 11:43,Alfonso Garcia-Caro通知@github.com
一个écrit:

太好了@EdouardM https://github.com/edouardm ,谢谢! 它会
如果你能写一个脚本来完成大部分转换,那就太好了
自动地。 如果您愿意,我们可以进行一个简短的屏幕共享会话
显示需要做什么:)


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/fable-compiler/Fable/issues/1370#issuecomment-375619541
或静音线程
https://github.com/notifications/unsubscribe-auth/AG_t3dil59Mz38u4cCN9f0JEZMyK7OLRks5thNHPgaJpZM4S03ig
.

我的电子邮件是:爱德华。 [email protected]

勒文。 2018 年 3 月 23 日 à 11:43,Alfonso Garcia-Caro通知@github.com
一个écrit:

太好了@EdouardM https://github.com/edouardm ,谢谢! 它会
如果你能写一个脚本来完成大部分转换,那就太好了
自动地。 如果您愿意,我们可以进行一个简短的屏幕共享会话
显示需要做什么:)


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/fable-compiler/Fable/issues/1370#issuecomment-375619541
或静音线程
https://github.com/notifications/unsubscribe-auth/AG_t3dil59Mz38u4cCN9f0JEZMyK7OLRks5thNHPgaJpZM4S03ig
.

不会包含在第一个 alpha 版本中的东西:bigint、反射、插件。

只是好奇,是否有路线图/计划在 Alpha 之后包含这些内容?

@jgrund没有具体的路线图,但 bigint 应该很容易添加(只需从 FSharp.Core 移植代码)并且反射也应该为稳定版本做好准备。 关于插件,我想收集有关它们如何使用的信息,然后共同决定我们应该如何提供它们或者是否需要它们。

谢谢你! 我确实必须对quicktest.sh做一些小改动才能让它在 Git-Bash 中工作:
https://github.com/fable-compiler/Fable/blob/07ddd104cbeb6ad7adf98b6600ac3fe4d1fcfd70/src/tools/quicktest.sh#L30 -L33
将第 32 行更改为:
./quickfsbuild.sh --no-build
因为那里的bash (我认为无论如何都不需要它?)导致 WSL 尝试启动,而我没有设置。

嗨@EdouardM! 这就是我们在 fsharpX 上讨论的关于更新测试的内容:

  • 使用 Expecto API(实际上会被翻译成 mocha for JS)
  • 使用正则表达式let ``(.*)``.*=和替换testCase "$1" <| fun () ->来更改测试的签名
  • 删除属性[<Test>] , [<TestFixture>]
  • 将文件添加到 test/Main/Main.fs 中的 Main.fsproj 和allTests
  • 在测试过程中需要特别注意异步和函数,我们可以稍后讨论。

@alfonsogarciacaro我正在阅读 dev2.0 分支的源代码,试图成为处理Replacement.fs其余部分的“疯狂者”,因为另外两个都被占用了😄。 我在从旧的Replacement.fs移植parse函数时遇到了一些问题, parse函数依赖于returnType ,它存在于寓言中的Fable.ApplyInfo 1.0,但我在CallInfo找不到它,不知道如何处理,你能给我一些帮助吗?

这是我的承诺:
https://github.com/fable-compiler/Fable/compare/dev2.0...zaaack :dev2.0?expand=1#diff-a34e57b30a53951ff9b99ce4076feaa4R1390

太好了@zaaack ,非常感谢。 是的,抱歉,我稍微更改了替换中函数的签名,使它们看起来更像其他模块中的函数,其中前三个参数通常是(上下文也可能排在第二位):

let myFableHelper (com: ICompiler) (range: SourceLocation option) (typ: Type) ... =

出于这个原因,我将返回类型(和范围)从CallInfo移到了函数的第一个参数。 您可以使用let parse com r t info thisArg args = (是的,我通常只使用t并且我同意它不是很有意义)。

我也在替换模块中工作。 我可以处理集合(Seq、List、Array),而你完成其他功能,你觉得如何?

我最近添加了一个Helper静态类型,以便使用带有可选参数的静态成员。 请查看现有代码以了解如何使用InstanceCallCoreCall ... 助手。 其他注意事项:

  • 正确传递参数类型(特别是如果有函数)对于稍后发生的非柯里化优化很重要。 如果未修改参数,则直接从CallInfo传递它们。 如果是,则必须重新计算 arg 类型。
  • ThisArg是可选的。 如果存在,它将首先通过。 在寓言 2 中,类型实例成员实际上被编译为静态函数,这就是this作为参数传递的原因。 ArgTypes不包括ThisArg
  • 但是,当指定InstanceCall kind 时, ThisArg表达式将被编译为实际实例调用: thisArg.member(arg1, arg2...)

现在应该足够了:) 如果您有任何问题,请与我联系,再次感谢您的帮助!

感谢您快速详细的回复,我明天试试。

我也在替换模块中工作。 我可以处理集合(Seq、List、Array),而你完成其他功能,你觉得如何?

太好了,我想我可以在工作日花两个小时,周末花一整天。

仅供参考,如果有人正在移植测试文件,则无需移植SetTests.fsMapTests.fs我将这样做来测试模块集成。

此外,如果您使用 VSCode,您可以使用多项选择在一个文件中一次性完成所有工作。 在这里编写脚本似乎有点过于工程化:)。

2018-04-18 15 14 27

谢谢 Maxime 我将处理测试文件的转换。
爱德华

2018 年 4 月 18 日 15:16,“Maxime Mangel”通知@ github.com 写道:

仅供参考,如果有人正在移植测试文件,则无需移植
SetTests.fs 和 MapTests.fs 我会用它来测试模块集成。

此外,如果您使用 VSCode,则可以使用多项选择来完成所有工作
一次在一个文件中。 编写脚本似乎有点过于工程化
这里 :)。

[图片:2018-04-18 15 14 27]
https://user-images.githubusercontent.com/4760796/38934173-59a5fc84-431b-11e8-81be-13f624e72817.gif


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/fable-compiler/Fable/issues/1370#issuecomment-382382463
或静音线程
https://github.com/notifications/unsubscribe-auth/AG_t3YjH5jvU1CXc-27gpz4-tGQNC7AHks5tpzyegaJpZM4S03ig
.

大家好! 我们仍然需要在src/js/fable-core/List.fs 中实现以下方法(注意它是一个 F# 文件,但它会在 JS 中分发):

  • [x] 截断
  • [x] 连接
  • [ ] 找回
  • [ ] 查找索引返回
  • [ ] 尝试查找返回
  • [ ] tryFindIndexBack
  • [x] 索引
  • [ ] 地图折叠
  • [ ] 地图折叠
  • [x] 尝试项目
  • [x] 展开
  • [x] splitAt

这些已实现,但测试未通过:

  • [x] foldBack2
  • [x] 分区

这些有点特别,因为 Fable 需要注入IEqualityComparer作为额外的参数。 我可以自己做,但如果你足够勇敢,你可以sort为例

  • [x] 不同
  • [x] distinctBy
  • [ ] 通过...分组
  • [ ] 计数
  • [ ] 包含
  • [ ] 除了

我已将失败的测试放在QuickTest.fs文件中,因此您应该能够重新编译 Fable + fable-core 并只需键入以下内容即可运行测试:

cd src/tools
sh quicktest.sh --build-core

有志愿者吗? ;) @ valery -vitko

我会看看这个周末我能做什么。

不幸的是,我在接下来的几周里都挤满了人。 (而且主要是与非编程相关的东西😢)。 没有我,玩得开心,伙计们。

我修复了 #1405 中的partitionfoldBack2
我会尝试做一些其他的低挂功能,做了truncateindexed

实现了unfoldtryItemsplitAtconcat

你好呀! 上面的列表中还有一些缺失的方法。 有人在研究它们吗? 如果没有,我会试一试:+1:

@alfonsogarciacaro对不起,我以为他们都被带走了,剩下的我来。

公关: https :

考虑处理方法和接口的新方式的问题; 以下将如何处理?

type Base () =
  abstract M: unit -> string
  default __.M () = "base"

type A () =
  inherit Base ()
  member __.M () = "a" // NOTE: Not override, hides parent M

type B () =
  inherit Base ()
  override __.M () = "b"

let a = A ()
let b = B ()

printfn "%s" <| (a :> Base).M ()
printfn "%s" <| (b :> Base).M ()

Fable 2 将自己的成员实现为模块函数并在编译时解析引用。 但是,虚拟方法附加到 JS 原型,因此即使在向上转换对象之后也可以调用它们。 所以这个稍微扩展的代码版本(以显示对象如何调用父成员):

type Base () =
  abstract M: unit -> string
  default __.M () = "base"

type A () =
  inherit Base ()
  member __.M () = base.M() + " + a" // NOTE: Not override, hides parent M

type B () = 
  inherit Base ()
  override __.M () = base.M() + " + b"

let a = A ()
let b = B ()

printfn "A:        %s" <| a.M ()
printfn "A upcast: %s" <| (a :> Base).M ()
printfn "B:        %s" <| b.M ()
printfn "B upcast: %s" <| (b :> Base).M ()

用最新的 dotnet-fable 2.0.0-alpha 编译成:

function Base() {}

function Base$$$$002Ector() {
  return this != null ? Base.call(this) : new Base();
}

Base.prototype.M = function () {
  return "base";
};

function A() {
  Base$$$$002Ector.call(this, null);
}

(0, _Types.inherits)(A, Base);

function A$$$$002Ector() {
  return this != null ? A.call(this) : new A();
}

function A$$M(__$$1) {
  return Base.prototype.M.call(this) + " + a";
}

function B() {
  Base$$$$002Ector.call(this, null);
}

(0, _Types.inherits)(B, Base);

function B$$$$002Ector() {
  return this != null ? B.call(this) : new B();
}

B.prototype.M = function () {
  return Base.prototype.M.call(this) + " + b";
};

const a = exports.a = A$$$$002Ector();
const b = exports.b = B$$$$002Ector();
(0, _String.toConsole)((0, _String.printf)("A:        %s"))(A$$M(a));
(0, _String.toConsole)((0, _String.printf)("A upcast: %s"))(a.M());
(0, _String.toConsole)((0, _String.printf)("B:        %s"))(b.M());
(0, _String.toConsole)((0, _String.printf)("B upcast: %s"))(b.M());

并打印:

A:        base + a
A upcast: base
B:        base + b
B upcast: base + b

@alfonsogarciacaro有没有办法完全禁用方法 -> 模块转换并获得干净的寓言 1 结果?

@vbfox不,抱歉。 这会影响寓言 2 的工作方式,不幸的是我没有资源来维护编译器标志以生成不同的输出:/

我完全理解资源问题,但很遗憾,因为它破坏了寓言和 JS 世界之间的许多兼容性,至少在任何地方使用类时:

  • 使用 fable 创建一个 npm 包供 JS 消费
  • 使用多种语言访问寓言生成的类(打字稿)或必须具有非常奇怪的绑定...

(外部库进行方法检测的情况也​​需要创建接口,但这主要是好的做法)

它也正在远离 JS,您至少可以为课程感到自豪 - 即使我们并不那么关心 ;)

是的,生成的代码在寓言 2 中会有些 _不那么漂亮_。但是我希望还有其他优点(更好的摇树、静态调度)可以弥补这一事实。 关于在 Fable 中创建用于 JS 消费的库,它仍然是可能的,尽管我还没有看到任何实际示例。 而且我已经放弃在我的寓言项目中使用 Typescript,因为每当我尝试导入 .fs 文件时它总是会编译;)

与JS互操作的方式应该是接口。 Fable 现在将为接口生成带有未修改名称的对象包装器,因此您可以使用它们将类型发送到 JS 或其他语言。

@alfonsogarciacaro我真的很想在用 Fable 制作的 JS 中构建用于消费的库,但这是一个很大的痛苦,我放弃了它。 这是我没有比现在更多地使用 Fable 的最大原因。 我得出的结论是它仅适用于构建应用程序(无论是否为网络)。

感谢@Alxandr 的确认。 所以它在寓言 1 中已经很痛苦了,这意味着寓言 2 不能让它变得更糟 ;)

@alfonsogarciacaro确实如此。 而且我不认为转向静态会很重要(因为界面对象是一个很好的解决方案)。 但我确实认为它可能值得讨论为什么它非常痛苦,因为我认为其中一些可能会被寓言 2 解决。我认为主要问题是寓言核心存在于 nuget 而不是 npm(js文件)。 这意味着如果我有一个库和一个用 F# 构建的应用程序,我现在有MapList等的两个副本。而且你也破坏了instanceof .. .

真的。 实际上我们曾经试图解决这个问题(我认为在 Fable 0.7 中,当 fable-core 和 Fable 本身通过 npm 分发时)但是 fable-core 改变了很多,正确管理版本是一个噩梦(我们还必须保留版本适用于不同的模块系统)。 情况要好得多,因为寓言 1 是寓言核心文件只是由编译器注入,我们始终使文件保持同步。 我认为为 npm 创建 Fable 库是不可行的。 您要么创建一个 JS 库(暴露一个 JS API 表面,最好是捆绑),要么编写一个通过 Nuget 分发的 Fable/F# 库。

JS 生态系统非常广泛,Fable 为 F# 提供了许多机会。 不幸的是,我的影响力有限,在做出设计决策时无法考虑所有因素。 到目前为止,通常在不同领域引入 Fable:Elmish Web 应用程序、React Native、Electron ……感谢创建应用程序/库/工具的贡献者,然后我们共同努力使体验更容易。 但是除了几个小样本之外,我还没有看到用 Fable 创建的 JS 库。

是的,不,我完全明白。 我的“一劳永逸”解决方案可能是这样一种情况:您在 CI 上构建 fable-core(andy F#/js 库)并同时发布到 NuGet 和 NPM。 这样你也不需要 nuget 上的 F# 源文件(你只有构建元数据,但你需要 npm 导入名称)。 尽管如此,需要将很多部分组合在一起才能工作,并且可能需要在 nuget 包中存储一堆元数据(映射到生成的 JS 函数名称)。

所以,我今天一直在思考新的寓言输出,我想出了一些我想提出的问题,以防它们没有被考虑:

类与函数

我自己并不是 JS 中的类的忠实粉丝,但是,它们确实服务于一个无法通过函数实现的重要目的(据我所知),即扩展本机类(如MapArray )。 现在,我假设使用函数而不是类的主要原因是您需要处理多个构造函数,但我有理由确定可以通过其他方式解决(我会再看看更多明天)。 现在,为了清楚起见,我不是在谈论回到将所有东西附加到原型上的问题。 我只是在谈论使主要(或合成)构造函数成为一个类。 至于在没有new情况下调用构造函数的能力,我认为这是一个相当有争议的问题,考虑到 F# 是一种类型化语言,而 Fable 应该能够静态地确定它是否需要发出new与否取决于它调用的函数。

接口转换和类型检查

我已经看到很好地处理类型层次结构的代码,并允许类型检查和调用虚拟成员,这一切都非常好,但是新的输出如何与类型检查推测的接口一起工作? 以下面的代码为例:

type IFoo =
  abstract Foo: string

type IBar =
  abstract Bar: string

type FooBar () =
  interface IFoo with
    member __.Foo = "foo"

  interface IBar with
    member __.Bar = "bar"

let check (t: bool) =
  if not t then failwithf "test failed"

let test () =
  let foobar = FooBar ()
  let foo = foobar :> IFoo
  let bar = foobar :> IBar
  let obj = foobar :> obj

  check (foo :? FooBar)
  check (bar :? FooBar)
  check (foo :? IBar)
  check (bar :? IFoo)
  check (obj :? IFoo)
  check (obj :? IBar)
  ()

我们现在可以关闭这个问题了,非常感谢大家帮助发布寓言2!

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

相关问题

rommsen picture rommsen  ·  3评论

alfonsogarciacaro picture alfonsogarciacaro  ·  3评论

tomcl picture tomcl  ·  4评论

MangelMaxime picture MangelMaxime  ·  3评论

ncave picture ncave  ·  3评论