在任意长度的嵌套元组上进行优雅的模式匹配

在任意长度的嵌套元组上进行优雅的模式匹配

本文介绍了在任意长度的嵌套元组上进行优雅的模式匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用F#开发一个可组合的功能UI库,遇到一种情况,我需要能够创建异类类型的项目的集合".我不想通过动态编程并将所有内容强制转换为obj来完成此操作(从技术上讲,在这里是可能的,因为我正在使用Fable进行编译).相反,我想保留尽可能多的类型安全性.

I'm developing a composable functional UI library in F#, and I ran into a situation where I need to be able to create "collections" of items of heterogenous types. I don't want to accomplish this by resorting to dynamic programming and casting everything to obj (technically possible here, esp. since I'm compiling with Fable). Instead I want to retain as much type safety as possible.

我想出的解决方案是创建一个简单的自定义运算符 %%% 来构建元组,然后按以下方式使用它:

The solution I came up with is to create a simple custom operator %%% that builds tuples and then use it as follows:

let x = 4 %%% "string" %%% () %%% 2.4

这将产生具有以下类型的值:

This produces a value with following type:

val x: (((int * string) * unit) * float)

生成的类型似乎有些混乱(尤其是随着值数量的增加),但是它可以确保我的方案具有强大的类型安全性,并且(在理想情况下)将对库的用户有所隐藏.

The resulting types seem a bit messy (especially as the number of values increases), but it ensures strong type safety for my scenario and will (ideally) be somewhat hidden to users of the library.

但是我试图找出一种优雅的方式来与这些嵌套元组类型进行模式匹配,因为库用户有时需要在这些值上编写函数.显然,这可以手动完成,例如,

But I'm trying to figure out an elegant way to pattern match with these nested tuple types, since the library users will sometimes need to write functions on these values. Obviously this could be done manually like,

match x with
| (((a,b),c),d) -> ...

,编译器会为 a b c d 推断正确的类型.但是,我不希望用户不必担心所有嵌套.我希望能够做类似的事情,

and the compiler infers the correct types for a, b, c, and d. However, I don't want the user to have to worry about all that nesting. I'd love to be able to do something like,

match x with
| a %%% b %%% c %%% d -> ...

,让编译器解决所有问题.有没有办法使用活动模式(或某些其他功能)使用F#完成类似的工作?

and have the compiler just figure everything out. Is there a way to accomplish something like that with F# using active patterns (or some other feature)?

我应该澄清,我不是在运行时尝试匹配未知"arity"的元组值.我只想在编译时知道元素的数量(和类型)时执行此操作.如果我做的是前者,那么采用动态方法就可以了.

I should clarify that I'm not trying to match on tuple values of unknown "arity" at runtime. I only want to do this when the number (and types) of elements is known at compile time. If I was doing the former, I'd be fine with a dynamic approach.

目前,我已经创建了活动模式:

For now, I've created active patterns:

let (|Tuple2|) = function | (a,b)-> (a,b)
let (|Tuple3|) = function | ((a,b),c) -> (a,b,c)
let (|Tuple4|) = function | (((a,b),c),d) -> (a,b,c,d)
...

可以这样使用:

let x = 4 %%% "string" %%% () %%% 2.4
let y = match x with | Tuple4 (a,b,c,d) -> ...

这可能是可以做到的最好的选择,并且对用户来说确实不是那么糟糕(只需要计算元组的"arity",然后使用正确的TupleN模式).但是,它仍然使我感到烦恼,因为它看上去并不像它应该的那么优雅.创建 x 时不必指定元素的数量,为什么要匹配它呢?对我来说似乎不对称,但我没有办法避免这种情况.

This is probably the best that can be done, and it's really not that bad for users (just have to count the "arity" of the tuple and then use the right TupleN pattern). However it still bugs me because it just doesn't seem as elegant as it could be. You don't have to specify the number of elements when creating x, why should you have to do so when matching on it? Seems asymmetric to me, but I don't see a way to avoid it.

我的原始想法在F#(或通常为静态类型的语言)中不起作用的原因更深层吗?是否有任何可能的功能语言?

Are there deeper reasons why my original idea won't work in F# (or statically typed languages in general)? Are there any functional languages where it would be possible?

推荐答案

您似乎正在尝试建立某种语义模型,尽管我不清楚它到底是什么.

It looks like you're attempting to build up a semantic model of some sort, although it's not entirely clear to me exactly what it is.

正如约翰·帕尔默(John Palmer)所暗示的那样,在静态类型的函数式编程语言中经常这样做的一种方式是定义一个类型来保存模型的异构值.在这种情况下,可能是这样的:

As John Palmer hints, a way this is often done in statically typed functional programming languages is to define a type to hold the heterogeneous values of the model. In this case, it could be something like this:

type Model =
| Integer of int
| Text of string
| Nothing
| Float of float

(为含糊的命名表示歉意,但如上所述,我不清楚您要建模的确切对象.)

(Apologies for the vague naming, but as stated, it's not clear to me exactly what you're trying to model.)

您现在可以建立以下类型的值:

You can now build up values of this type:

let x = [Integer 4; Text "string"; Nothing; Float 2.4]

在这种情况下, x 的类型为 Model list .现在,您可以在以下类型上轻松进行模式匹配的数据类型:

In this case, the type of x is Model list. You now have a data type you can trivially pattern match on:

match x with
| [Integer i; Text s; Nothing; Float f] -> ...

如果您能提供比我在此处选择的名称更好的名称,这甚至可以使API变得有用且直观.

If you can come up with better names than the ones I chose here, this may even make the API useful and intuitive.

这篇关于在任意长度的嵌套元组上进行优雅的模式匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-29 05:20