[英]Equivalent to C# generic interface in Haskell
I'm trying to convert some simple C# code to Haskell. 我正在尝试将一些简单的C#代码转换为Haskell。 So say I've got a simple immutable "database" type that is just a record with various list fields.
所以说我有一个简单的不可变“数据库”类型,它只是一个包含各种列表字段的记录。 So, say
所以,说吧
data Person = Person { }
data Book = Book { }
data Database = Database { employees :: [Person], books :: [Book], customers :: [Person] }
Now I want to create a typeclass that represents a "view", or essentially a "table" of that DB. 现在我想创建一个类型类,表示“视图”,或者本质上是该数据库的“表”。
class Table r t where -- r is the record type (e.g. Person or Book)
getRecords :: t -> Database -> [r]
setRecords :: t -> [r] -> Database -> Database
Then I can create instances that represent each of those tables: 然后我可以创建代表每个表的实例:
data ET = EmployeeTable
instance (Table Person) ET where
getRecords t db = employees db
setRecords t records db = Database records (books db) (customers db)
This is what I have, and it works, but only if {-# LANGUAGE MultiParamTypeClasses #-}
is included. 这就是我所拥有的,并且它可以工作,但
{-# LANGUAGE MultiParamTypeClasses #-}
是包含{-# LANGUAGE MultiParamTypeClasses #-}
。 Otherwise the definition of the Table typeclass fails. 否则,表类型类的定义失败。
Not a big deal in itself: it compiles and works, but a quick read on MultiParamTypeClasses
alludes to potential complications down the line (I haven't taken the time to fully grok them yet). 本身并不是什么大不了的事:它可以编译和工作,但是快速阅读
MultiParamTypeClasses
可以解决潜在的复杂问题(我还没有花时间完全搞定它们)。
The weird thing for me though is that this is very straightforward in C#. 对我来说奇怪的是,这在C#中非常简单。 Assuming simple immutable definitions of the record/DB class, it's simple to define the interface, and then the implementations follow without issue.
假设记录/数据库类的简单不可变定义,定义接口很简单,然后实现遵循没有问题。
interface ITable<TRecord> {
TRecord[] GetRecords(Database db);
Database SetRecords(TRecord[] records, Database db);
}
So really that's the essence of this question. 真的,这就是这个问题的本质。 Is there a more idiomatic way to translate the functionality accorded by the above
ITable<TRecord>
interface from C# to Haskell? 是否有更惯用的方法将上述
ITable<TRecord>
接口所赋予的功能从C#转换为Haskell? My understanding is that C# interfaces are closest to Haskell typeclasses, so that's what I'm trying to do. 我的理解是C#接口最接近Haskell类型类,所以这就是我想要做的。 But I find it suprising that something as simple as a generic interface requires a language extension in the highly-touted type system in Haskell.
但我觉得很奇怪,像通用接口这样简单的东西需要在Haskell中高度吹捧的类型系统中进行语言扩展。
(NB why do I want to do this? The above is a bit simplified for brevity's sake, but in general, if I make the fleshed-out Person
and Book
instances of a Record
typeclass that just has getId
, then I can support CRUD functions for the whole database very generically: I only have to define these functions once for the Table typeclass, and they'll apply to all tables in the DB automatically. Here's the full code a sample usage of it at the bottom, and its C# equivalent. https://gist.github.com/daxfohl/a785d1ff72b921d7e90b70f625191a1c . Note in Haskell deleteRecord
doesn't compile either since the type of r
cannot be deduced, whereas in C# it compiles fine. This adds to my thought that maybe MultiParamTypeClasses
is not the right approach. But if not, then what is ?) (NB 为什么我要这样做?为简洁起见,上面的内容有点简化,但一般来说,如果我创建了一个只有
getId
的Record
类型类的充实Person
和Book
实例,那么我可以支持CRUD函数对于整个数据库非常一般:我只需要为Table类型类定义一次这些函数,它们将自动应用于DB中的所有表。这里是完整代码底部的示例用法,以及它的C#等价物.https: deleteRecord
。注意Haskell中的deleteRecord
不能编译,因为r
的类型无法推断,而在C#中它编译得很好。这增加了我的想法,也许MultiParamTypeClasses
不是正确的做法。但如果不是,那又是什么?)
Okay it sounds from the comments like MultiParamTypeClasses
is fine. 好吧,它听起来像
MultiParamTypeClasses
这样的评论很好。 So now my remaining question is how to fix the linked gist such that deleteRecord
will compile? 所以现在我剩下的问题是如何修复链接的要点,以便
deleteRecord
将编译?
deleteRecord :: (Table r t) => t -> Int -> Database -> Database
This is a problem. 这是个问题。 There's an
r
before the =>
but no r
after the =>
. 这里有一个
r
的前=>
但没有r
后=>
。 Given a function call like deleteRecord bookTable 1 db
, Haskell has no idea which r
you are talking about. 给定一个函数调用像
deleteRecord bookTable 1 db
,哈斯克尔不知道哪些r
你在说什么。 Though r
should be completely determined by t
, Haskell has no way of knowing that. 虽然
r
应该完全由t
决定,但Haskell无法知道这一点。 Indeed, no one disallows these instance definitions: 实际上,没有人不允许这些实例定义:
instance Table Foo Bar
instance Table Foo Baz
instance Table Qux Bar
instance Table Qux Baz
There is nothing in the "table" types that could prevent this. “表”类型中没有任何内容可以阻止这种情况。 They are just empty tags without any real data.
它们只是没有任何实际数据的空标签。
The fact that there is only one instance of interest in your module is irrelevant, Haskell cannot guarantee anything about other modules. 事实上,您的模块中只有一个感兴趣的实例是无关紧要的,Haskell无法保证其他模块的任何内容。
So what are your options here? 那你有什么选择呢?
FunctionalDependencies
. FunctionalDependencies
。 TypeFamilies
. TypeFamilies
。 The last two extensions allow for creation of generic containers among other things, which is what your database tables essentially are. 最后两个扩展允许创建通用容器,这是数据库表本质上的。
Here's how Table
type class would look with the first extension: 以下是
Table
类型在第一个扩展名中的外观:
class Record r => Table r t | t -> r where ...
The notation t -> r
means that r
is completely determined by t
(IOW r
functionally depends on t
). 符号
t -> r
表示r
完全由t
确定(IOW r
功能上取决于 t
)。 Once Haskell sees an instance Table Foo Bar
, it knows there can be no instance Table Qux Bar
(the compiler will signal an error should it see a conflicting definition). 一旦Haskell看到一个实例
Table Foo Bar
,就会知道没有实例Table Qux Bar
(编译器会在看到相互冲突的定义时发出错误信号)。 This way deleteRecord
is well-formed. 这样
deleteRecord
就是格式良好的。 r
is not in the signature but it's OK: t
is known and r
is a function of t
. r
不在签名中但是没关系: t
是已知的并且r
是t
的函数。
I let you figure out TypeFamilies
by yourself. 我让你自己弄清楚
TypeFamilies
。 It's a more popular solution these days. 如今这是一种更受欢迎的解决方案。
FunctionalDependencies
is an older extension, it is simple to understand but can lead to complications in some corner cases. FunctionalDependencies
是一个较旧的扩展,它易于理解,但在某些极端情况下会导致复杂化。 Don't worry about them, you will be a master of Haskell before you see any. 不要担心它们,在你看到之前你将成为Haskell的主人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.