在运行时选择实例行为 [英] Select instance behavior at runtime

查看:184
本文介绍了在运行时选择实例行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直试图在运行时从众多的实例中选择一个实例。真的是一种后端



我能够做到这一点,如果我选择一个实例或其他编译时间。



更新可能我想要一些类似于 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>

我无法为 selectedBee

使用函数。

任何帮助将不胜感激!)

GADTs ,但没有额外的 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


解决方案

为什么要使用类型类?相反,将typeclass表示为记录类型,其中instances是该类型的值:

  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

(我没有编译过这段代码,但应该可以)



类型类是为了在编译时完全解决。你试图强制它们在运行时被解析。相反,想想你是如何真正在OOP中指定它的:你有一个类型和一个函数,根据它的参数返回该类型的某个值。然后您调用该类型的方法。唯一的区别是,对于OOP解决方案,从选择函数返回的值不具有该函数应该显示的确切类型,因此您要返回 BehaviorA BehaviorB 而不是 IBehavior 。使用Haskell,你必须实际返回一个与返回类型完全匹配的值。



OOP版本让你做Haskell的唯一不是将你的 IBehavior 回到 BehaviorA BehaviorB ,无论如何,这被视为不安全。如果您收到的类型是由接口指定的值,则应始终将自己限制为仅允许该接口允许的值。哈斯克尔强制这一点,而OOP仅仅通过惯例来使用它。有关此模式的更完整说明,请查看 发布。


I'm stuck trying to select one instance from many at runtime. Really is a kind of Backend.

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 GADTs works but I think exist a better way (full code at the bottom).

In some OOP language my problem is more or less

interface IBehavior { void foo(); }

class AppObject { IBehavior bee; void run(); }

...
  var app = new AppObject { bee = makeOneOrOtherBehavior(); }
....

I've tried many ways (and lots of extensions :D) but none works.

Informally I want to define one class with certain behavior and use this generic definition into some application, after it, select at runtime one instance from some.

The generic behavior (not real code)

class Behavior k a where
  behavior :: k -> IO ()
  foo :: k -> a -> Bool
  ...

(I think k is needed since each instance could need their own context/data; other restrictions like key/value may be exist)

Two instances

data BehaviorA
instance Behavior BehaviorA where
  behavior _ = print "Behavior A!"

data BehaviorB
instance Behavior BehaviorB where
  behavior _ = print "Behavior B!"

my application use that behavior (here begin the chaos)

data WithBehavior =
  WithBehavior { foo :: String
               , bee :: forall b . Behavior b => b
               }

run :: WithBehavior -> IO ()
run (WithBehavior {..}) = print foo >> behavior bee

I wish select at runtime

selectedBee x = case x of
                  "A" -> makeBehaviorA
                  "B" -> makeBehaviorB
                  ...
withBehavior x = makeWithBehavior (selectedBee x)

but I'm lost into a maze of extensions, type dependencies and others :(

I cannot set the proper type for selectedBee function.

Any help will be appreciated! :)

(Using 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

解决方案

Why use a typeclass? Instead, represent the typeclass as a record type, with "instances" being values of that type:

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

(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 BehaviorA or BehaviorB instead of an IBehavior. With Haskell you have to actually return a value that exactly matches the return type.

The only thing that the OOP version lets you do that Haskell doesn't is cast your 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.

这篇关于在运行时选择实例行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆