2010-07-22 76 views
3

我有一个交易的单子,看起来像:交易类型安全

newtype Transaction t m a = .. my monad stack here ..

t是我使用,以确保我环比上升适用于同一后台存储的交易幻像类型。

我的主循环的迭代使用类型,如:

Transaction DB IO (Widget (Transaction DB IO()))

上述类型的意思是:“其生成UI窗口小部件,其用户输入转换为交易执行交易”。

此外,我有:

data Property m a = Property { get :: m a, set :: a -> m() }

这是非常有用的(特别是由于它与FCLabels撰写能力

我做一个可怕的很多使用Property (Transaction t m)产生我的交易。

现在,我想要的类型系统,以保证生成的插件的事务是只读事务和交易Ť他的widget生成被允许为读写事务。

显然,我可以只添加另一个幻象类型的交易,但是这有两个问题:

  • 它需要显式转换从Transaction ReadOnlyTransaction ReadWrite或类型级两轮牛车允许之间的一元组成读和写基元。这我想我可以解决。

  • 属性数据构造函数为了包含写入器,总是需要一个读/写操作来强制“m”成为一个ReadWrite事务。我无法在读写和只读事务的上下文中重新使用属性。

我正在寻找一种获得上述类型安全性的好方法,而不会丢失设计的上述特性。

+2

正如您指出的那样,只要具有像'liftRORW :: Transaction ReadOnly t m a - > Transaction ReadWrite t m a'这样的函数,用于跟踪只读/读写的幻像类型可以解决问题。这似乎是一个非常可靠的方法。 我不认为我理解你提出的“财产”问题。 – intoverflow 2010-07-23 00:03:19

+0

改变幻像类型的转换确实很重要,但只需要很少使用。大多数情况下,您可以创建一个函数,该函数可以是幻像类型中免费的ReadOnly或ReadWrite块的一部分。 这是一个穷人的子类型。 – sclv 2010-07-23 01:09:51

+0

此外,您可以更改Property为: 'data Property m a = Property {get :: forall free。 m free a,set :: a - > m ReadWrite()}' 虽然我不知道与那个和FCLabels互操作。 – sclv 2010-07-23 01:15:18

回答

3

如果您希望类型检查器在只读和读写事务之间实施区分,那么这两者必须是不同的类型。从那里开始工作,该解决方案呈现为:

data Property rm wm a = Property { get :: rm a, set :: a -> wm() } 

此方法有很多变体。相反,不同的单子,你可以有不同的上下文参数的单子:

newtype Transaction t c m a = .. my monad stack here 

data Property mc c1 c2 a = Property { get :: mc c1 a, set :: a -> mc c2() } 

这里mc是一个单子的构造函数;它需要上下文参数来创建monad。尽管这使用了更多的参数,但我更喜欢它,因为它强调了monads的相似性。

对于需要读取或写入的函数,请考虑使用类型类。

newtype ReadOnly = ReadOnly 

newtype ReadWrite = ReadWrite 

class ReadContext rm where 

class WriteContext rm where 

instance ReadContext ReadOnly where 
instance ReadContext ReadWrite where 

instance WriteContext ReadWrite where 

someGetter :: ReadContext c => Transaction t c m a 

someSetter :: WriteContext c => a -> Transaction t c m() 

这应该限制铸件的量/解除你需要做的,同时还强制类型安全。

+0

伟大的想法,谢谢:-) – Peaker 2010-07-23 20:43:36