在声明之前使用函数

时间:2012-11-30 13:31:39

标签: f#

为了在F#中使用文字编程(即cweb),我需要能够转发声明函数(即在定义它们之前使用它们)。我提出了两种方法,两种方式都令人不快。你能想到更好的东西(程序员更容易使用)吗?

很好,但不适用于多态函数

// This can be ugly
let declare<'a>  = ref Unchecked.defaultof<'a>

// This has to be beautiful
let add = declare<float -> float>

let ``function I want to explain that depends on add`` nums = nums |> Seq.map !add

add := fun x -> x + 1.

丑陋,但适合一切

// This can be ugly
type Literate() =
static member Declare<'a, 'b>  (ref : obj ref) (x : 'a) : 'b =
                                    unbox <| (unbox<obj -> obj> !ref)
static member Define<'a, 'b> (func : 'a -> 'b) (ref : obj ref) (f : 'a -> 'b) =
                                    ref := box (unbox<'a> >> f >> box)

// This has to be beautiful
let rec id (x : 'a) : 'a = Literate.Declare idImpl x
and idImpl = ref null

let f () = id 100 + id 200

Literate.Define id idImpl (fun x -> x)

4 个答案:

答案 0 :(得分:3)

在创建www.tryjoinads.org时,我使用的工具遵循与文字编程相同的想法。文档只是一个带有代码片段的Markdown,它会变成一个可以运行的F#源代码,并且片段必须按正确的顺序排列。 (在一些有文化的编程工具中,文档是在提交中编写的,但想法是一样的。)

现在,我认为让您的代码更复杂以便您可以用文化编程风格编写它(并记录它)会引入很多偶然的复杂性并且它会破坏主要目的文学编程。

所以,如果我想解决这个问题,我会用一些注释扩展我的文字编程工具,该注释指定使脚本工作所需的代码块的顺序(并且一个简单的预处理工具可以重新排序它们在生成F#输入时)。您可以为TryJoinads [查看我的构建脚本] [1],这可以很容易地扩展到这样做。

我用于TryJoinads的工具已经提供了一些元标记,可用于隐藏输出中的代码块,因此您可以编写如下内容:

## This is my page heading

    [hide]
    // This function will be hidden from the generated HTML
    // but it is visible to the F# compiler
    let add a b = a + b 

Here is the description for the other function:

   let functionThatUsesAdd x = add x x

And later on I can repeat `add` with more comments (and I can add an annotation
`module` to avoid conflicts with the previous declaration):

   [module=Demo]
   let add a b =
     // Add a and b
     a + b

这也不完美,因为你必须复制功能,但至少你生成的博客文章或HTML文档不会被无关紧要的事物所掩盖。但是,当然,添加一些元命令如modulehide来指定块的顺序并不会太难,这将是一个干净的解决方案。

总之,我认为你只需要一个更好的文化编程工具,而不是不同的F#代码或F#langauge。

答案 1 :(得分:1)

也许我错过了一些东西,但为什么你不能一路走下去'正确地做'?

  

首先使用该功能:

<<test.fs>>=
<<add>>
let inc = add 1
     

之后声明该功能:

<<add>>=
let add a b = a + b

答案 2 :(得分:0)

由于函数是F#中的第一类对象,因此您可以传递它们 - 这提供了比前向引用更好(并且仍然不可变)的解决方案。

let dependentFunction f nums = nums |> Seq.map f

let ``function I want to explain that depends on add`` nums =
    dependentFunction (fun x -> x + 1.) nums

此外,在大多数情况下,您应该能够使用currying(部分函数应用程序)来进一步简化代码,但seq<'T>的类型推断在F#中有点奇怪,因为它通常用作灵活类型(类似于C#4.0中的协方差。举例说明:

// This doesn't work because of type inference on seq<'T>,
// but it should work with most other types.
let ``function I want to explain that depends on add`` =
    dependentFunction (fun x -> x + 1.)

最后,在F#中使用refmutable的一个好的经验法则是,如果你只想分配一次值(初始化它),那么可能会更清洁,更多编写该代码的功能方式(将值作为函数参数传递(如上所述)和lazy是两种这样的方法)。显然这个规则有例外,但即便如此,它们应该非常谨慎地使用。

答案 3 :(得分:0)

正如我所说,这是错误的(你应该发表一篇博客文章,或者一篇关于你为什么这样做的FPish帖子)),但这是我的看法:

let ``function I want to explain that depends on add``
      (add : float -> float) = nums |> Seq.map add

let add = (+) 1.

let ``function I want to explain that depends on add`` = ``function I want to explain that depends on add`` add