在运行时选择实例行为 [英] Select instance behavior at runtime
问题描述
后端
。 我能够做到这一点,如果我选择一个实例或其他编译时间。
更新可能我想要一些类似于 Database.Persist (它定义了一个完全的行为,但是很多实例:mongodb,sqlite,postgresql,...)。但对我来说太复杂了。使用 GADTs
可行,但我认为存在
$ b
UPDATED 一个更好的方法(完整的代码在底部)。
在一些OOP语言中,我的问题是或多或少的
interface IBehavior {void foo(); }
class AppObject {IBehavior bee; void run(); }
...
var app = new AppObject {bee = makeOneOrOtherBehavior(); }
....
我尝试了很多方法(和很多扩展: D)但没有任何作用。
非正式地,我想定义一个 class
并且使用这个泛型定义一些应用程序,在它之后,在运行时选择一个实例
从一些。
通用行为(不是真正的代码)
class行为ka其中
行为:: k - > IO()
foo :: k - > a - > Bool
...
(我认为 k $因为每个
)实例
可能需要它们自己的上下文/数据,所以需要c $ c>;其他限制如 key
/ <$可能存在c $ c> value
两个实例
data BehaviorA
实例行为BehaviorA其中
行为_ =打印行为A!
数据行为B
实例行为BehaviorB其中
行为_ =打印行为B!
我的应用程序使用这种行为(这里开始混乱)
数据WithBehavior =
WithBehavior {foo :: String
,bee :: forall b。行为b => b
}
run :: WithBehavior - > IO()
run(WithBehavior {..})= print foo>>行为蜜蜂
我希望在运行时选择
selectedBee x = case x of
A - > makeBehaviorA
B - > makeBehaviorB
...
withBehavior x = makeWithBehavior(selectedBee x)
但是我陷入了扩展迷宫,类型依赖和其他问题:($ / b>
我无法为 使用 任何帮助将不胜感激!) 为什么要使用类型类?相反,将typeclass表示为记录类型,其中instances是该类型的值: (我没有编译过这段代码,但应该可以) 类型类是为了在编译时完全解决。你试图强制它们在运行时被解析。相反,想想你是如何真正在OOP中指定它的:你有一个类型和一个函数,根据它的参数返回该类型的某个值。然后您调用该类型的方法。唯一的区别是,对于OOP解决方案,从选择函数返回的值不具有该函数应该显示的确切类型,因此您要返回 OOP版本让你做Haskell的唯一不是将你的 I'm stuck trying to select one instance from many at runtime. Really is a kind of I'm able to do it if I select one instance or other at compile time. UPDATED probably I want some similar to Database.Persist (it define a fully behavior but many instances: mongodb, sqlite, postgresql, ...). But is too complex to me. UPDATED using In some OOP language my problem is more or less I've tried many ways (and lots of extensions :D) but none works. Informally I want to define one The generic behavior (not real code) (I think Two instances my application use that behavior (here begin the chaos) I wish select at runtime but I'm lost into a maze of extensions, type dependencies and others :( I cannot set the proper type for Any help will be appreciated! :) (Using
Why use a typeclass? Instead, represent the typeclass as a record type, with "instances" being values of that type: (I haven't compiled this code, but it should work) Typeclasses are meant to be resolved fully at compile time. You're trying to force them to be resolved at runtime. Instead, think about how you're really specifying it in OOP: you have a type and a function that returns some value of that type based on its arguments. You then call a method on that type. The only difference is that with the OOP solution the values returned from the selection function don't have the exact type that the function says it should, so you're returning a The only thing that the OOP version lets you do that Haskell doesn't is cast your 这篇关于在运行时选择实例行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! selectedBee $ c $设置适当的类型
函数。
,但没有额外的
a
类型参数!)
{ - #LANGUAGE RecordWildCards# - }
{ - #LANGUAGE GADTs# - }
import System.Environment
import Control.Applicative
class行为k其中
行为':: k - > IO()
data BehaviorInstance其中
BehaviorInstance :: Behavior b => b - > BehaviorInstance
behavior :: BehaviorInstance - > IO()
行为(BehaviorInstance b)=行为'b
数据BehaviorA =行为A
实例行为BehaviorA其中
行为'_ = print行为A!
makeBehaviorA :: BehaviorInstance
makeBehaviorA = BehaviorInstance BehaviorA
data BehaviorB = BehaviorB
instance BehaviorB其中
behavior'_ = printBehavior B!
makeBehaviorB :: BehaviorInstance
makeBehaviorB = BehaviorInstance BehaviorB
数据WithBehavior =
WithBehavior {foo :: String
,bee :: BehaviorInstance
}
run :: WithBehavior - > IO()
run(WithBehavior {..})= print foo>>行为蜜蜂
main = do
n< - head< $> getArgs
let be = case $ n
A - > makeBehaviorA
_ - > makeBehaviorB
运行$ WithBehaviorFoo消息! be
data行为ka =行为
{behaviour :: IO()
,foo :: k - > a - > Bool
}
behaviorA :: Behavior String Int
behaviorA =行为
{behavior = putStrLn行为A!
,foo = \ a b - >长度a < b
}
behaviorB :: Behavior String Int
behaviorB = Behavior
{behavior = putStrLnBehavior B!
,foo = \ a b - >长度a> b
}
selectBehavior :: String - >可能(Behavior String Int)
selectBehaviorA=只是行为a
selectBehaviorB=只是行为b
selectBehavior _ =没有
main :: IO()
main = do
putStrLn哪种行为(A或B)?
选择< - getLine
let selected = selectBehavior选择
可能(返回())行为被选中
putStrLn你叫什么名字?
名称< - getLine
putStrLn你的年龄是?
age < - readLn - 不要在真实代码中使用,您应该实际解析事物
也许(return())(\bhvr - > print $ foo bhvr name age)selected
BehaviorA
或 BehaviorB
而不是 IBehavior
。使用Haskell,你必须实际返回一个与返回类型完全匹配的值。
IBehavior
回到 BehaviorA
或 BehaviorB
,无论如何,这被视为不安全。如果您收到的类型是由接口指定的值,则应始终将自己限制为仅允许该接口允许的值。哈斯克尔强制这一点,而OOP仅仅通过惯例来使用它。有关此模式的更完整说明,请查看 发布。Backend
.GADTs
works but I think exist a better way (full code at the bottom).interface IBehavior { void foo(); }
class AppObject { IBehavior bee; void run(); }
...
var app = new AppObject { bee = makeOneOrOtherBehavior(); }
....
class
with certain behavior and use this generic definition into some application, after it, select at runtime one instance
from some.class Behavior k a where
behavior :: k -> IO ()
foo :: k -> a -> Bool
...
k
is needed since each instance
could need their own context/data; other restrictions like key
/value
may be exist)data BehaviorA
instance Behavior BehaviorA where
behavior _ = print "Behavior A!"
data BehaviorB
instance Behavior BehaviorB where
behavior _ = print "Behavior B!"
data WithBehavior =
WithBehavior { foo :: String
, bee :: forall b . Behavior b => b
}
run :: WithBehavior -> IO ()
run (WithBehavior {..}) = print foo >> behavior bee
selectedBee x = case x of
"A" -> makeBehaviorA
"B" -> makeBehaviorB
...
withBehavior x = makeWithBehavior (selectedBee x)
selectedBee
function.GADTs
, but without additional a
type parameters!){-# 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
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
BehaviorA
or BehaviorB
instead of an IBehavior
. With Haskell you have to actually return a value that exactly matches the return type.IBehavior
back to a BehaviorA
or BehaviorB
, and this is often considered unsafe anyway. If you receive a value whose type is specified by an interface, you should always restrict yourself to only what that interface allows. Haskell forces this, while OOP uses it merely by convention. For a more complete explanation of this pattern check out this post.