2

为了在 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

4 回答 4

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# 语言。

于 2012-11-30T19:06:25.210 回答
1

也许我错过了一些东西,但你为什么不一直走下去并“正确地做”?

先使用函数:

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

之后声明函数:

<<add>>=
let add a b = a + b
于 2012-12-03T06:51:30.970 回答
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.)

ref最后,在 F# 中使用or的一个好的经验法则mutable是,如果您只分配一次值(初始化它),那么可能有一种更简洁、更实用的方式来编写代码(将值作为函数传递参数(如上),lazy是两种这样的方法)。显然,这条规则也有例外,但即便如此,也应该非常谨慎地使用它们。

于 2012-11-30T15:30:35.193 回答
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
于 2012-11-30T15:34:51.633 回答