(背景:我一直在考虑做关于F#和函数式编程的演示。从经验来看,我认为模式匹配和类型推断的'哇'因素不一定足以抵消'帮助' !'因素'我的大括号和分号在哪里,我的代码将脱离边缘!“。这让我想到真正令人惊叹的因素 - 对我来说 - 这是1)如果它编译,通常意味着它的工作原理和2)您经常可以从类型推断实现
Channel9与Brian Beckman和Erik Meijer有一个video,他们在那里提到了实现有时只是“掉出”函数的类型签名。我过去也经历过这种情况,但是无法提出一个很好的例子,可以很容易地呈现给没有任何功能经验的人。
有没有人有一个很好的例子可以分享? (它不必在F#中)
更新
如果有任何帮助,我认为我们需要以不同的方式思考:实际的难题如下:
我有一些给定类型的数据,我想将它转换为另一种类型,我有一组函数给定的符号。
这是你必须插在一起的'lego'。
答案 0 :(得分:6)
从最简单的功能开始:identity :: 'a -> 'a
。你能想到多少个实现?如果你给我一个a
,我只能做一件事就是给你一个a
。我给你回复你给我的a
,所以:
let id x = x
配对也一样。 fst :: ('a,'b) -> 'a
。你有多少种方法可以实现? snd :: ('a, 'b) -> 'b
怎么样?每种方法只能存在一种实现方式。
类似地,取一个列表的头部和尾部落在fst
和snd
之外。如果head :: 'a list -> a
和tail :: 'a list -> 'a list
以及'a list
只是一对('a, 'a list)
(或空列表),那么显然要满足这些类型,您将返回第一个和第二个列表的一部分。
另一个与高阶函数有关的示例:compose :: ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
。只有一个实现,它完全属于类型。您将获得c
和两个函数。你能用c
做什么?那么,你可以申请(c -> a)
。你可以用a
做什么?你唯一能做的就是申请(a -> b)
,瞧,你已经满意了。
let compose f g x = f (g x)
答案 1 :(得分:5)
这是第三个例子......
假设我想写一个函数
p : 'a -> ('a -> 'b) -> 'b
也就是说,我将一个类型为a的值作为参数,并将一个带有'a并返回'b的函数作为参数。我的结果应该是'b。好吧,再次,modulo无限循环和异常以及默认初始化,只有一个实现:
let p x f = f x
在您意识到它是管道运算符(|>)之前,'p'可能看起来不太有用。
嗯,我觉得到目前为止这些例子并没有给人留下深刻的印象。答案 2 :(得分:5)
还要考虑几个......
om2 : ('a -> 'b) -> 'a option -> 'b option
唯一有趣的实现是Option.map:
let om f xo =
match xo with
| None -> None
| Some x -> Some(f x)
现在我可以写了
let om (f:'a -> 'b) (xo:'a option) : 'b option =
None
并忽略两个参数并始终返回None。但这并不有趣。有人向我们传递了所有这些有用的论据,当然我们打算用它们做点什么,对吧?因此,上面的第一个实现是唯一的一个(再次模拟其他答案中提到的小事,由于循环,效果等)。
类似地
lm : ('a -> 'b) -> 'a list -> 'b list
你很难写出除List.map以外的任何东西。你总是可以返回空列表,但是它会忽略这两个参数。你可以写
let lm f xs =
match xs with
| [] -> []
| h::t -> [f h]
但是有人传递这个完整有用的列表似乎很奇怪,我们忽略了除第一个元素之外的所有内容。如果您认为“意味着”'使用'所有数据,List.map就是明显的实现。 (虽然没有什么可以阻止你映射两次或三次并返回一个2x或3x与原始列表一样长的新列表。再一次,有一种感觉/美学,其中有一个'最简单明显'的实现与类型签名匹配并使用传入的数据,这个“明显”的实现是有用的。当你看到签名
('a -> 'b) -> 'a list -> 'b list
你只是想'List.map',甚至没有人会考虑所有其他理论上可能的实现,因为其他的在软件工程环境中几乎都是无意义的。)
我不知道这是否有说服力。
答案 3 :(得分:4)
假设我想写一个函数f
f : 'a -> 'a
在F#中,我认为唯一有趣的实现是:
let f x = x
上面的身份函数是大多数语言的自然实现。
正如在我的另一个答案中,你也可以循环,抛出或默认初始化。但那些不那么有趣。那些“后门”在所有这些“类型衍生”计算的工作中都会有所作为,所以最好忽略它们。
这个中的关键是,对于所有类型'a',你对类型一无所知,所以“获得”该类型的真实对象的唯一方法是让某人已经给你一个。因此,身份功能是该签名的唯一合理实现。
答案 4 :(得分:3)
好的,这不是一个非常好的例子,但是'好',也许会有助于获得其他一些想法......
假设
f : unit -> 'a
也就是说,我想编写一个不传递参数的函数,它返回任何类型的值。这个功能有什么作用?
请注意,我不能只返回'new obj()',因为类型签名是通用的,例如我可以用f< int>来调用它然后返回一个int,例如。
放弃?这是最常见的可能性:
let rec f() = f()
这是一个无限循环。它永远不会返回,因此返回类型无关紧要。你可以做很多种语言。
在像Haskell这样的语言中,“例外”将是由类型系统控制的“效果”,但在F#中:
let f() = failwith "kaboom!"
是另一个例子。如果我们再次抛出异常,则返回类型无关紧要。
最后,许多运行时的实现细节允许任何类型的“默认初始化”,例如,在F#
let f() = Unchecked.defaultof<'a>
也可以。我想也许这些是F#中唯一可能的三种实现方式。
答案 5 :(得分:2)
(a -> b) -> [a] -> [b]
实际上这种类型就是实现。
答案 6 :(得分:2)
愚蠢的首发示例,继续我的更新:
假设我有一个List<string>
,我怎样才能使用当前函数进入Array<float>
(其中一个函数被混淆了!)
fn1: string -> float
fn2: List<'a> -> Array<'a>
fn3: Array<'a> -> List<'a>
fn4: ('a -> 'b) -> Array<'a> -> Array<'b>
好吧,让我们看看:
//Start at the beginning
let input:List<string> = myData
// the only thing that I can apply to this is a
// function that starts with List<something>, so...
input |> fn2 // List<'a> -> Array<'b>, so now I have Array<string>
// At this point, it looks like I have no choice but fn3, but that takes
// me back where I came from. However, there is an Array<'a> in
// the middle of fn4.
input |> fn2 |> fn4 ???
//oops, I need something else here, a function that goes ('a -> 'b).
// But all my functions go ('a -> 'b)! However, in this case my 'a is a string,
// so that limits my choice to fn1:
input |> fn2 |> fn4 fn1 // so here I have Array<float> yoohoo!
//recapitulate with the real function names
let convert input =
input |> Array.ofList //List<string> -> Array<string>
|> Array.map float //Array<string> -> (string -> float) -> Array<float>
答案 7 :(得分:2)
见
Best way to condense a list of option type down to only elements that are not none?
简而言之,目标是在这里解决f
:
> [Some 4; None; Some 2; None] |> f;;
val it : int list = [4; 2]
(只展示Some
中的list
值的函数。我对解决方案的评论(List.choose id
)是
让这些类型指导你。您需要一个在其签名中包含选项的函数,但只返回一个列表(不是选项列表)。看看API,并且只有一个这样的函数,
choose
,现在你已经95%了。
答案 8 :(得分:0)
另一个答案来自Wes Dyer,他在解释Select for IObservable时提到了这个精确的现象。签名是:
IObservable<U> Select<T, U>(this IObservable<T> source, Func<T, U> selector)
我会让你解决这个问题...(我对自己非常满意,我设法写了自己的选择和在哪里)