为了在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)
答案 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文档不会被无关紧要的事物所掩盖。但是,当然,添加一些元命令如module
或hide
来指定块的顺序并不会太难,这将是一个干净的解决方案。
总之,我认为你只需要一个更好的文化编程工具,而不是不同的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#中使用ref
或mutable
的一个好的经验法则是,如果你只想分配一次值(初始化它),那么可能会更清洁,更多编写该代码的功能方式(将值作为函数参数传递(如上所述)和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