jos*_*uan 3 polymorphism haskell
我试图在运行时从多个实例中选择一个实例.真的是一种Backend.
如果我在编译时选择一个实例或其他实例,我就能做到.
更新可能我想要一些类似于Database.Persist(它定义一个完全行为,但很多实例:mongodb,sqlite,postgresql,...).但对我来说太复杂了.
使用GADTs作品更新,但我认为存在更好的方式(底部的完整代码).
在一些OOP语言中,我的问题或多或少
interface IBehavior { void foo(); }
class AppObject { IBehavior bee; void run(); }
...
var app = new AppObject { bee = makeOneOrOtherBehavior(); }
....
Run Code Online (Sandbox Code Playgroud)
我尝试了很多方法(以及很多扩展:D)但没有一种方法可行.
非正式地,我想要定义class具有特定行为的一个,并将此通用定义用于某个应用程序,之后,在运行时选择一个instance.
通用行为(不是真正的代码)
class Behavior k a where
behavior :: k -> IO ()
foo :: k -> a -> Bool
...
Run Code Online (Sandbox Code Playgroud)
(我认为k是必要的,因为每个人instance都需要他们自己的上下文/数据;其他限制如key/ value可能存在)
两个例子
data BehaviorA
instance Behavior BehaviorA where
behavior _ = print "Behavior A!"
data BehaviorB
instance Behavior BehaviorB where
behavior _ = print "Behavior B!"
Run Code Online (Sandbox Code Playgroud)
我的应用程序使用该行为(这里开始混乱)
data WithBehavior =
WithBehavior { foo :: String
, bee :: forall b . Behavior b => b
}
run :: WithBehavior -> IO ()
run (WithBehavior {..}) = print foo >> behavior bee
Run Code Online (Sandbox Code Playgroud)
我希望在运行时选择
selectedBee x = case x of
"A" -> makeBehaviorA
"B" -> makeBehaviorB
...
withBehavior x = makeWithBehavior (selectedBee x)
Run Code Online (Sandbox Code Playgroud)
但我迷失了迷宫的扩展,类型依赖和其他:(
我无法为selectedBee功能设置正确的类型.
任何帮助将不胜感激!:)
(使用GADTs,但没有其他a类型参数!)
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE GADTs #-}
import System.Environment
import Control.Applicative
class Behavior k where
behavior' :: k -> IO ()
data BehaviorInstance where
BehaviorInstance :: Behavior b => b -> BehaviorInstance
behavior :: BehaviorInstance -> IO ()
behavior (BehaviorInstance b) = behavior' b
data BehaviorA = BehaviorA
instance Behavior BehaviorA where
behavior' _ = print "Behavior A!"
makeBehaviorA :: BehaviorInstance
makeBehaviorA = BehaviorInstance BehaviorA
data BehaviorB = BehaviorB
instance Behavior BehaviorB where
behavior' _ = print "Behavior B!"
makeBehaviorB :: BehaviorInstance
makeBehaviorB = BehaviorInstance BehaviorB
data WithBehavior =
WithBehavior { foo :: String
, bee :: BehaviorInstance
}
run :: WithBehavior -> IO ()
run (WithBehavior {..}) = print foo >> behavior bee
main = do
n <- head <$> getArgs
let be = case n of
"A" -> makeBehaviorA
_ -> makeBehaviorB
run $ WithBehavior "Foo Message!" be
Run Code Online (Sandbox Code Playgroud)
为什么要使用类型类?相反,将类型类表示为记录类型,"instances"是该类型的值:
data Behavior k a = Behavior
{ behavior :: IO ()
, foo :: k -> a -> Bool
}
behaviorA :: Behavior String Int
behaviorA = Behavior
{ behavior = putStrLn "Behavior A!"
, foo = \a b -> length a < b
}
behaviorB :: Behavior String Int
behaviorB = Behavior
{ behavior = putStrLn "Behavior B!"
, foo = \a b -> length a > b
}
selectBehavior :: String -> Maybe (Behavior String Int)
selectBehavior "A" = Just behaviorA
selectBehavior "B" = Just behaviorB
selectBehavior _ = Nothing
main :: IO ()
main = do
putStrLn "Which behavior (A or B)?"
selection <- getLine
let selected = selectBehavior selection
maybe (return ()) behavior selected
putStrLn "What is your name?"
name <- getLine
putStrLn "What is your age?"
age <- readLn -- Don't use in real code, you should actually parse things
maybe (return ()) (\bhvr -> print $ foo bhvr name age) selected
Run Code Online (Sandbox Code Playgroud)
(我没有编译这段代码,但它应该工作)
类型编程意味着在编译时完全解决.您试图强制它们在运行时被解析.相反,想想你是如何在OOP中真正指定它的:你有一个类型和一个函数,它根据它的参数返回该类型的某些值.然后,您调用该类型的方法.唯一的区别是,使用OOP解决方案时,从选择函数返回的值不具有函数所说的确切类型,因此您将返回a BehaviorA或BehaviorB而不是IBehavior.使用Haskell,您必须实际返回与返回类型完全匹配的值.
OOP版本允许你做的唯一一件事就是Haskell没有把它抛IBehavior回到BehaviorA或者BehaviorB,这通常被认为是不安全的.如果您收到类型由接口指定的值,则应始终将自己限制为仅允许该接口允许的值.Haskell强迫这一点,而OOP仅仅按惯例使用它.有关此模式的更完整说明,请查看此文章.
| 归档时间: |
|
| 查看次数: |
250 次 |
| 最近记录: |