繁体   English   中英

Haskell匹配构造类似于F#类型测试模式?

[英]Haskell match construct analogous to F# type-test pattern?

TL;博士

如果我理解正确,Haskell没有像F#那样的子类型。 因此我希望它没有匹配的类型测试模式,比如F#。 它是否有任何可用于元编程的类似结构?

案例分析

我有一个F#项目 ,我在其中解耦了通过消息传递机制进行通信的模块。 最近我一直想知道这个代码会是什么样子,或者如果我将它移植到Haskell,它甚至可以用这种方式编写它。

基本的想法是这样的。 消息是从消息接口继承的类型:

type Message = interface end

处理程序是一个函数,它接受Message的特定子类型并返回一个Message

type Handle<'TMsg when 'TMsg :> Message> = 'TMsg -> Message

有一个带有publish方法的Bus ,它将消息分发到其内部通道。 每种消息类型都有一个Channel<'TMsg> ,这些是在注册处理程序时动态添加的。 总线将所有消息发布到所有通道,如果类型错误,则通道只返回一个空序列:

type Channel<'TIn when 'TIn :> Message>(...) = ...
    interface Channel with
        member x.publish (message : Message) =
            match message with
            | :? 'TIn as msg ->
                Seq.map (fun handle -> handle msg) _handlers
                |> Seq.filter (fun msg -> msg <> noMessage)
            | _ -> Seq.empty

最终我在这里做的是动态元编程,它允许我有强类型的消息,仍然通过中间的相同机器。 我不像H#那样依赖Haskell,我无法弄清楚如何在Haskell中做到这一点。

我是对的Haskell没有match... with... :?... as...构造? 它是否有类似的构造或其他方法来进行这种元编程,你可以解决这类问题吗? 是否有某种盒子/拆箱机制?

抱歉用Mu回答你的问题,但我也不会像在F#那样做。

Message接口就是所谓的标记接口 ,虽然这是有争议的,但我认为它是任何支持注释的语言(.NET中的属性 )的代码味道。

标记接口不执行任何操作,因此您可以在没有它的情况下实现相同的行为。

我不会在C#中设计类似的消息传递系统,因为标记接口没有添加任何内容,除了错误消息以某种方式“键入”。

我不会在F#中设计类似的消息传递系统,因为在F#中有一个更好,更安全,强类型的替代方案:

type UserEvent =
    | UserCreated of UserCreated
    | EmailVerified of EmailVerified
    | EmailChanged of EmailChanged

这清楚地说明了事件集是有限的,并且是众所周知的。 有了它,您将获得编译时检查而不是运行时检查。

(有关完整示例,请参见此处 。)

这样一个Discriminated Union很容易转换为Haskell:

data UserEvent = UserCreated UserCreatedData
               | EmailVerified EmailVerifiedData
               | EmailChanged EmailChangedData

Haskell的设计使得类型可以在运行时完全擦除。 也就是说,当实现将类型T的值存储在内存中时,不需要用一些标签来标记它“这是T类型”。

这给出了很好的参数保证,也被称为“自由定理”。 例如,具有多态类型的函数

f :: a -> a

必须返回其参数或未能终止。 也就是说,我们知道f=id (如果我们知道它终止)。

具体地说,这意味着没有办法写出类似的东西

f :: a -> a
f x = if a == Int then x+1 else x

如果有人想做这样的事情,可以通过Data.Typeable类手动添加类型标签:

{-# LANGUAGE ScopedTypeVariables, GADTs, TypeOperators #-}
import Data.Typeable

f :: forall a . Typeable a => a -> a
f x = case eqT :: Maybe (a :~: Int) of
      Just Refl -> x+1
      Nothing   -> x

注意类型是如何更改的,现在有一个约束。 它必须,因为函数的效果违反了无约束多态类型的自由定理。

因此,如果您确实需要执行运行时类型检查,请执行Typeable约束。 请注意,这可能过于笼统。 如果你知道你有少量的类型,可能你可以使用sum类型,并在构造函数上使用普通模式匹配来测试类型:

data T = TInt Int
       | TChar Char

f :: T -> T
f (TInt i) = TInt (i+1)
f (TChar c) = TChar c

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM