Kei*_*son 10 f# haskell types casting pattern-matching
如果我理解正确,Haskell没有像F#那样的子类型.因此我希望它没有匹配的类型测试模式,比如F#.它是否有任何可用于元编程的类似结构?
我有一个F#项目,我在其中解耦了通过消息传递机制进行通信的模块.最近我一直想知道这个代码会是什么样子,或者如果我将它移植到Haskell,它甚至可以用这种方式编写它.
基本的想法是这样的.消息是从消息接口继承的类型:
type Message = interface end
Run Code Online (Sandbox Code Playgroud)
处理程序是一个获取特定子类型的函数,Message并返回Message:
type Handle<'TMsg when 'TMsg :> Message> = 'TMsg -> Message
Run Code Online (Sandbox Code Playgroud)
有Bus一种publish方法可以将消息分发到其内部通道.有一个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
Run Code Online (Sandbox Code Playgroud)
最终我在这里做的是动态元编程,它允许我有强类型的消息,仍然通过中间的相同机器.我不像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
Run Code Online (Sandbox Code Playgroud)
这清楚地说明了事件集是有限的,并且是众所周知的.有了它,您将获得编译时检查而不是运行时检查.
这样一个Discriminated Union很容易转换为Haskell:
data UserEvent = UserCreated UserCreatedData
| EmailVerified EmailVerifiedData
| EmailChanged EmailChangedData
Run Code Online (Sandbox Code Playgroud)
Haskell的设计使得类型可以在运行时完全擦除.也就是说,当一个实现T在内存中存储一个类型的值时,不需要用一些标签来标记它"这是类型T".
这给出了很好的参数保证,也被称为"自由定理".例如,具有多态类型的函数
f :: a -> a
Run Code Online (Sandbox Code Playgroud)
必须返回其参数或未能终止.也就是说,我们知道f=id(如果我们知道它终止).
具体地说,这意味着没有办法写出类似的东西
f :: a -> a
f x = if a == Int then x+1 else x
Run Code Online (Sandbox Code Playgroud)
如果有人想做这样的事情,可以通过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
Run Code Online (Sandbox Code Playgroud)
注意类型是如何更改的,现在有一个约束.它必须,因为函数的效果违反了无约束多态类型的自由定理.
因此,如果您确实需要执行运行时类型检查,请执行Typeable约束.请注意,这可能过于笼统.如果你知道你有少量的类型,可能你可以使用sum类型,并在构造函数上使用普通模式匹配来测试类型:
data T = TInt Int
| TChar Char
f :: T -> T
f (TInt i) = TInt (i+1)
f (TChar c) = TChar c
Run Code Online (Sandbox Code Playgroud)