【问题标题】:Hidden Features of F#F#的隐藏特性
【发布时间】:2010-09-15 22:43:54
【问题描述】:

这是对类似C# 问题的大胆尝试。

那么您最喜欢的 F# 隐藏(或不隐藏)功能是什么?

到目前为止,我使用的大多数功能都没有完全隐藏,但令人耳目一新。与 C# 或 VB.NET 相比,重载运算符是多么微不足道。

Async<T> 帮助我去除了一些真正丑陋的代码。

我对这门语言还是很陌生,所以很高兴了解在野外使用的其他功能。

【问题讨论】:

    标签: f# language-features hidden-features


    【解决方案1】:

    没有隐藏功能,因为 F# 处于设计模式。我们拥有的只是技术预览版,每两个月更改一次。

    见http://research.microsoft.com/fsharp/

    【讨论】:

    • 我非常猜测因此每个功能都以某种方式隐藏了 ;)
    【解决方案2】:

    是的,F# 没有任何“隐藏”功能,但它确实在简单语言中包含了很多功能。该语言的一个鲜为人知的特性是,尽管 F# 是静态类型的,但您基本上可以enable duck typing。

    【讨论】:

      【解决方案3】:

      代数数据类型的自动生成比较函数(基于字典顺序)是一个相对未知的好特性;见

      http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!548.entry

      举个例子。

      【讨论】:

        【解决方案4】:

        将 F# 用作实用程序脚本语言可能不受欢迎。 F# 爱好者往往是 Quant。有时,您希望备份 MP3(或数十个数据库服务器)的功能比批处理功能更强大。我一直在寻找 jscript / vbscript 的现代替代品。最近,我使用了 IronPython,但 F# 可能更完整,.NET 交互也不那么麻烦。

        我喜欢curried functions 的娱乐价值。向至少三个 WTF 的纯过程/OOP 程序显示柯里化函数。不过,从这里开始是获得 F# 转换的不好方法:)

        【讨论】:

        • 我喜欢将 FSI 用于小脚本,而且我实际上使用了一些 .fsx 文件。但是,至少根据我的经验,.fsx 文件所需的启动时间对于通用脚本来说太长了。我仍然更喜欢批处理文件和 Perl 脚本来编写脚本。
        • 试试 PowerShell,你不会后悔 :)
        【解决方案5】:

        看到这个问题

        F# operator "?"

        有关问号运算符的信息,以及它如何提供基本语言机制来构建类似于 C# 中“动态”的功能。

        【讨论】:

          【解决方案6】:

          F# 有一个很少使用的功能,称为“签名文件”。您可以拥有一个包含 public 类型/方法/模块/函数的大型实现文件,但随后您可以通过签名文件。也就是说,签名文件充当一种屏幕/过滤器,使您能够使实体“对该文件公开”但“对程序的其余部分保密”。

          我觉得这是 .Net 平台上的一个非常棒的功能,因为用于这种封装的唯一其他/先前工具是程序集。如果您有一个包含一些相关类型的小组件,希望能够看到彼此的内部详细信息,但不希望这些类型将所有这些位公开给每个人,你能做什么?好吧,你可以做两件事:

          1. 您可以将该组件放在单独的程序集中,并将这些类型共享的成员设为“内部”,并将您希望其他人看到的狭窄部分设为“公开”,或者
          2. 您只需将内部内容标记为“内部”,但将这些类型留在庞大的程序集中,并希望程序集中的所有其他代码选择不调用那些仅标记为“内部”的成员,因为需要另一种类型去看看。

          根据我的经验,在大型软件项目中,每个人总是做 #2,因为 #1 由于各种原因无法启动(人们不想要 50 个小程序集,他们想要 1 或 2 或 3 个大程序集,因为与我提出的封装点无关的其他可能很好的理由(除此之外:每个人都提到 ILMerge,但没有人使用它)。

          所以你选择了选项 #2。然后一年后,你终于决定重构那个组件,你发现在过去的一年里,其他 17 个地方现在调用了那个“内部”方法,它实际上只适用于另一种类型调用,使它真正很难考虑到这一点,因为现在每个人都依赖于这些实现细节。无赖。

          关键是,在 .Net 中没有创建中等大小的程序集内封装范围/边界的好方法。很多时候“内部”太大而“私有”太小。

          ...直到 F#。使用 F# 签名文件,您可以通过在实现文件中将一堆东西标记为公共来创建“此源代码文件”的封装范围,因此文件中的所有其他代码都可以看到它并参与其中,但随后使用隐藏所有细节的签名文件期望组件向世界其他地方公开的狭窄公共接口。这是快乐的。在一个文件中定义三个高度相关的类型,让他们看到彼此的实现细节,但只将真正公开的东西暴露给其他人。赢了!

          签名文件可能不是组件内封装边界的理想功能,但它们是我所知道的唯一这样的功能,所以我像一个海洋中的救生筏。

          TL;DR

          复杂性是敌人。封装边界是对付这个敌人的武器。 “私有”是一个很好的武器,但有时太小而无法应用,“内部”通常太弱,因为太多代码(整个程序集和所有 InternalsVisibleTo 的)可以看到内部的东西。 F# 提供的作用域大于“类型私有”但小于“整个程序集”,这非常有用。

          【讨论】:

            【解决方案7】:

            泛型类型的内联运算符可以有不同的泛型约束:

            type 'a Wrapper = Wrapper of 'a with
              static member inline (+)(Wrapper(a),Wrapper(b)) = Wrapper(a + b)
              static member inline Exp(Wrapper(a)) = Wrapper(exp a)
            
            let objWrapper = Wrapper(obj())
            let intWrapper = (Wrapper 1) + (Wrapper 2)
            let fltWrapper = exp (Wrapper 1.0)
            
            (* won''t compile *)
            let _ = exp (Wrapper 1)
            

            【讨论】:

            • 澄清一下,如果 (+) 和 Exp 未声明为内联会发生什么?
            • @Joh:运算符将不再是多态的,因此例如,您只能在程序中的单一类型的 Wrapper 上使用 (+) 运算符。我上面写的所有代码仍然可以工作,但是使用内联运算符,我们还可以添加两个float Wrappers。
            【解决方案8】:

            可以通过提供一个名称以NumericLiteral 开头并定义某些方法(FromZero、FromOne 等)的模块来定义用户定义的数字文字。

            特别是,您可以使用它为调用LanguagePrimitives.GenericZero 和LanguagePrimitives.GenericOne 提供更易读的语法:

            module NumericLiteralG = begin
              let inline FromZero() = LanguagePrimitives.GenericZero
              let inline FromOne() = LanguagePrimitives.GenericOne
            end
            
            let inline genericFactorial n =
              let rec fact n = if (n = 0G) then 1G else n * (fact (n - 1G))
              fact n
            
            let flt = genericFactorial 30.
            let bigI = genericFactorial 30I
            

            【讨论】:

            • 好主意 - 应该默认启用。
            • 这是一个很好的技巧,但它似乎只适用于 G、N、Q、R 和 Z。
            【解决方案9】:

            将--warnon:1182 传递给编译器会打开未使用变量的警告;以下划线开头的变量名是免疫的。

            【讨论】:

            • 谢谢!这不在编译器选项页面上!真正隐藏。
            • 有趣的是,从 F# 2.0 开始,编译器会发出以下警告:FSC : warning FS0075: The command-line option '--warnon' is for internal use only
            【解决方案10】:

            不是真的隐藏,但作为一个非机器学习的人,这让我很长时间都没有意识到:

            模式匹配可以将任意深度分解为数据结构。

            这是一个 [令人难以置信的任意] 嵌套元组示例;这适用于列表或联合或嵌套值的任何组合:

            let listEven =
              "Manipulating strings can be intriguing using F#".Split ' '
              |> List.ofArray
              |> List.map (fun x -> (x.Length % 2 = 0, x.Contains "i"), x)
              |> List.choose 
                 ( function (true, true), s -> Some s 
                          | _, "F#"         -> Some "language" 
                          | _               -> None ) 
            

            【讨论】:

              【解决方案11】:

              我想知道如果你添加会发生什么

              <appSettings>
                <add key="fsharp-navigationbar-enabled" value="true" />
              </appSettings>
              

              到您的 devenv.exe.config 文件? (使用风险自负。)

              【讨论】:

              • 有没有机会让我们知道而不需要我们实际尝试?
              • 我有什么理由不应该留下这个吗?很高兴拥有。你知道它是否会在 vNext 中吗?
              • 它没有经过很好的测试,可能会使VS更容易崩溃。还不知道它是否会出现在 vNext 中......也许还有“解决方案导航器”,我们要看看我们能适应什么时间表......
              猜你喜欢
              • 2011-03-19
              • 1970-01-01
              • 1970-01-01
              • 2010-11-07
              • 2010-11-07
              • 2010-09-22
              • 2010-10-31
              • 2011-01-30
              • 2010-10-31
              相关资源
              最近更新 更多