当前位置 : 主页 > 手机开发 > 其它 >

haskell – 功能依赖与类型族

来源:互联网 收集:自由互联 发布时间:2021-06-22
我正在开发一个用于运行人工生命实验的框架,我正在尝试使用类型系列而不是函数依赖项.类型族似乎是 Haskellers中的首选方法,但我遇到了一种功能依赖似乎更合适的情况.我错过了一招
我正在开发一个用于运行人工生命实验的框架,我正在尝试使用类型系列而不是函数依赖项.类型族似乎是 Haskellers中的首选方法,但我遇到了一种功能依赖似乎更合适的情况.我错过了一招吗?这是使用类型系列的设计. (此代码编译正常.)

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}

import Control.Monad.State (StateT)

class Agent a where
  agentId :: a -> String
  liveALittle :: Universe u => a -> StateT u IO a
  -- plus other functions

class Universe u where
  type MyAgent u :: *
  withAgent :: (MyAgent u -> StateT u IO (MyAgent u)) -> 
    String -> StateT u IO ()
  -- plus other functions

data SimpleUniverse = SimpleUniverse
  {
    mainDir :: FilePath
    -- plus other fields
  }

defaultWithAgent :: (MyAgent u -> StateT u IO (MyAgent u)) -> String -> 
  StateT u IO ()
defaultWithAgent = undefined -- stub

-- plus default implementations for other functions

--
-- In order to use my framework, the user will need to create a typeclass
-- that implements the Agent class...
--

data Bug = Bug String deriving (Show, Eq)

instance Agent Bug where
  agentId (Bug s) = s
  liveALittle bug = return bug -- stub

--
-- .. and they'll also need to make SimpleUniverse an instance of Universe
-- for their agent type.
--

instance Universe SimpleUniverse where
  type MyAgent SimpleUniverse = Bug
  withAgent = defaultWithAgent     -- boilerplate
  -- plus similar boilerplate for other functions

有没有办法避免强迫我的用户写最后两行样板?与下面使用fundeps的版本相比,这似乎使我的用户更简单. (使用UndecideableInstances可能是一个红旗.)(此代码也可以编译好.)

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,
    UndecidableInstances #-}

import Control.Monad.State (StateT)

class Agent a where
  agentId :: a -> String
  liveALittle :: Universe u a => a -> StateT u IO a
  -- plus other functions

class Universe u a | u -> a where
  withAgent :: Agent a => (a -> StateT u IO a) -> String -> StateT u IO ()
  -- plus other functions

data SimpleUniverse = SimpleUniverse
  {
    mainDir :: FilePath
    -- plus other fields
  }

instance Universe SimpleUniverse a where
  withAgent = undefined -- stub
  -- plus implementations for other functions

--
-- In order to use my framework, the user will need to create a typeclass
-- that implements the Agent class...
--

data Bug = Bug String deriving (Show, Eq)

instance Agent Bug where
  agentId (Bug s) = s
  liveALittle bug = return bug -- stub

--
-- And now my users only have to write stuff like...
--

u :: SimpleUniverse
u = SimpleUniverse "mydir"

编辑:在尝试提供一个简单的例子时,我省略了我设计的部分动机.

Universe类扮演的#1角色是序列化和反序列化代理,所以我认为它必须链接到Agent类.它还具有readAgent和writeAgent函数.但是,我想确保用户在修改代理后不会意外忘记编写代理,因此我提供了一个处理所有内容的withAgent函数,而不是导出这些函数. withAgent函数有两个参数:在代理上运行的函数,以及运行程序的代理的名称(唯一ID).它读取包含该代理的文件,运行该程序,并将更新的代理写回文件. (我可以只导出readAgent和writeAgent函数.)

还有一个守护进程类,负责为每个代理提供其公平的CPU份额.因此,在守护程序的主循环中,它会查询Universe以获取当前的代理列表.然后,对于每个代理程序,它调用withAgent函数来为该代理程序运行liveAlittle程序.守护进程不关心代理的类型.

withAgent函数还有另一个用户:代理本身.在代理的liveALittle函数内部,它可能会查询Universe以查找代理列表,以便成为可能的交配伙伴.它将调用withAgent函数来运行某种交配功能.显然,代理只能与同一物种的另一个代理(类型类)交配.

编辑:这是我认为我将使用的解决方案.不输入族或函数依赖,但现在我必须做一些事情,以便编译器知道要调用哪个liveALittle.我这样做的方法是让用户提供正确的liveALittle作为参数.

{-# LANGUAGE DeriveGeneric #-}

import Control.Monad.State (StateT)
import Data.Serialize (Serialize)
import GHC.Generics (Generic)

class Agent a where
  agentId :: a -> String
  liveALittle :: Universe u => a -> StateT u IO a
  -- plus other functions

class Universe u where
  -- Given the name of an agent, read it from a file, and let it run.
  withAgent :: (Agent a, Serialize a) => 
    (a -> StateT u IO a) -> String -> StateT u IO ()
  -- plus other functions

-- This method will be called by a daemon
daemonTask :: (Universe u, Agent a, Serialize a) => 
  (a -> StateT u IO a) -> StateT u IO ()
daemonTask letAgentLiveALittle = do
  -- do some stuff
  withAgent letAgentLiveALittle "a"
  -- do some other stuff

data SimpleUniverse = SimpleUniverse
  {
    mainDir :: FilePath
    -- plus other fields
  }

instance Universe SimpleUniverse where
  withAgent = undefined -- stub
  -- plus implementations for other functions

--
-- And now my users only have to write stuff like...
--

data Bug = Bug String deriving (Show, Eq, Generic)

instance Serialize Bug

instance Agent Bug where
  agentId (Bug s) = s
  liveALittle bug = return bug -- stub
我认为你太复杂了.支持宇宙中的每一种演员并不复杂,它不那么复杂.

只需编写您的Universe类:

class Universe u where
  withAgent :: Agent a => (a -> StateT u IO a) -> String -> StateT u IO ()

请注意,您不必使用函数依赖项或多参数类型类,因为不必将a带入类头中的范围;它由代理a =>进入范围. ….这也基本上是你在功能依赖版本中所做的,因为即使你使用你的| a你 – > a,a实际上没有在课堂体内使用;相反,代理a => …遮蔽外面的a.

网友评论