Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 我可以强制类型构造函数中存在的量化参数吗?_Haskell_Types_Gadt_Existential Type - Fatal编程技术网

Haskell 我可以强制类型构造函数中存在的量化参数吗?

Haskell 我可以强制类型构造函数中存在的量化参数吗?,haskell,types,gadt,existential-type,Haskell,Types,Gadt,Existential Type,我有一个数据类型,其(单个)构造函数包含一个存在量化的类型变量: data LogEvent = forall a . ToJSON a => LogEvent { logTimestamp :: Date , logEventCategory :: Category , logEventLevel :: LogLevel ,

我有一个数据类型,其(单个)构造函数包含一个存在量化的类型变量:

data LogEvent = forall a . ToJSON a =>
            LogEvent { logTimestamp     :: Date
                     , logEventCategory :: Category
                     , logEventLevel    :: LogLevel
                     , logThreadId      :: ThreadId
                     , logPayload       :: a
                     }
当我最初编写该类型时,我隐藏了多态负载,因为当时我感兴趣的是输出到某个文件/流。但是现在我想做更多有趣的事情,我需要观察
a
的实际类型

我从和其他阅读资料中了解到,存在量化的类型变量在每次实例化时都是唯一的。但是,给定的类型是
ToJSON a
我可以使用如下类似的代码(伪代码):

能够以更精确的类型在JSON之间进行转换似乎有些奇怪,尽管我可以理解这背后的原理

那么,如果我知道它的类型,如何重写该类型以允许提取
logPayload
?我

那么,如果我知道logPayload的类型,如何重写该类型以允许提取logPayload呢

如果您不想更改您的类型,您可以将
fromJSON&toJSON
替换为
unsafeccerce
–如果您是正确的,同样的想法,同样的结果,但是如果您对类型不正确,可能会导致程序崩溃

如果希望类型检查器确保正确,则必须在
LogEvent
中公开类型
a
,而不使用存在类型。

这类似于(反)模式。这种存在的魔力相当于

data LogEvent = 
        LogEvent { logTimestamp     :: Date
                 , logEventCategory :: Category
                 , logEventLevel    :: LogLevel
                 , logThreadId      :: ThreadId
                 , logPayload       :: Aeson.Value
                 }
但这更清楚地传达了您的结构所代表的内容。你不应该从你的存在结构中期待任何你不会从中期待的东西

另一方面,如果您确实知道
logPayload
的类型,则应通过将类型变量移出,在类型级别对该知识进行编码:

data LogEvent a = ...
此时,类型为
LogPayload Foo
的值表示您对有效负载类型的了解。那么,如果你有这样的倾向,你可以定义

data ALogEvent = forall a. ToJSON a => ALogEvent (LogEvent a)
当你不知道的时候。在实践中,我很少看到这两者都存在的必要性,但也许你有一个这样的用例


如果您在运行时知道
logPayload
的类型,但由于某种原因无法在编译时跟踪有效负载,那么您可以在存在约束中添加一个
Typeable
约束,这样您就可以进行强制转换,而无需诉诸
unsafeCoerce
。。。万一你犯了一个错误,你就不会怪罪你的整个程序。

< P>你可以考虑给出<代码>数据。在存在类型中加入一个
Typeable
约束,然后如果你能正确猜出隐藏类型,你就可以在该类型下得到值。有关玩具示例,请参见


请注意,如果您开始在
LogEvent
中输入另一个类型,则此技术会牺牲一定数量的类型安全性,而您之前没有这样做,因为您可能会中断该类型的用户,他们认为他们成功地处理了每个子类。与代数类型不同,动力学和强制类型转换意味着编译器无法帮助您证明穷尽性。

我在这里使用存在包装器,因为事件通过
Chan LogEvent
异步记录:一个单独的线程读取事件并处理它。当然,我最初尝试使用有效负载类型对
LogEvent
类型进行参数化,但没有得到任何结果,因为通道另一端的使用者仍然无法观察事件的类型。如果使用者需要观察事件的类型,我建议使用代数类型。也就是说,除非它统一处理所有事件(保存一两个特殊情况),在这种情况下,Typeable可能是rightYes,但这意味着将
LogEvent
类型与记录的事件绑定,我想避免但显然不能再推迟的事情:-)这不是库代码,所以我不太在乎,尽管这意味着让日志模块依赖于系统的每种记录类型,这有点糟糕…@insitu,如果日志记录器需要观察事件类型,记录器已具有此依赖项,不是吗?确定:-),但不是在所有记录的类型上。对此,您可能会回答:为您需要观察的类型提供类型化构造函数,您可能是对的!让我们试试……在luqui的评论之后,我开始沿着这条路线走,但后来遇到了一堵墙:不能将eta简化为表单实例的实例(…)=>在“Command”的数据实例声明中,我要记录的类型实际上是在typeclass中定义的类型族的实例,似乎
DeriveDataTypeable
对此有限制。或者我可能会误解编译器错误
data ALogEvent = forall a. ToJSON a => ALogEvent (LogEvent a)