如何让PureScript中不同的record type类型不相等?
在PureScript中实现标称化的Record类型以保证类型安全
PureScript中使用type定义的Record类型本质是类型别名,仅为底层结构类型赋予新名称,因此结构相同的别名类型会被编译器视为同一类型,自然可以直接比较。要实现类似Haskell中data Foo = Foo {a :: Int}的标称类型(Nominal Type)效果,让结构相同的不同类型无法直接比较,你可以使用newtype来包装Record。
解决方案代码示例
module Main where import Prelude import Effect (Effect) import Effect.Console (log) import Data.Eq (Eq) -- 用newtype包装Record,创建独立的标称类型 newtype Foo = Foo { a :: Int } -- 派生Eq实例,让Foo类型支持相等比较 derive newtype instance eqFoo :: Eq Foo newtype Bar = Bar { a :: Int } derive newtype instance eqBar :: Eq Bar foo :: Foo foo = Foo { a: 1 } bar :: Bar bar = Bar { a: 1 } main :: Effect Unit main = do -- 编译报错:无法比较Foo和Bar类型的值 log $ show $ foo == bar
效果说明
上述代码中,Foo和Bar是完全独立的标称类型,即使它们内部的Record结构完全一致,编译器也会阻止跨类型的相等比较,符合你预期的类型安全要求。
访问内部Record字段
如果需要访问包装后的Record字段,可以通过模式匹配或Data.Newtype模块提供的工具:
import Data.Newtype (class Newtype, unwrap) -- 实现Newtype实例,方便通用地解包 derive newtype instance newtypeFoo :: Newtype Foo _ derive newtype instance newtypeBar :: Newtype Bar _ -- 解包获取内部Record fooRecord = unwrap foo -- 直接访问字段 fooA = case foo of Foo r -> r.a
与Haskell实现的对比
Haskell中data Foo = Foo {a :: Int}的方式会生成带Record语法的代数数据类型,而PureScript的newtype是更轻量的选择——它在运行时不会带来额外的性能开销,仅在编译时提供类型区分,是实现标称化Record的最优方案。
内容的提问来源于stack exchange,提问作者Hi-Angel
相关产品推荐
相关产品推荐

