1

我正在尝试编写一个相当简单的处理器模拟器,并试图将实际处理器与其内存接口分开。例如,旧的 TRS-80 有 12K 的 ROM,可以配置总共 16、32 或 48K 的总 RAM。我最初对内存系统的想法是一个类型类:

class MemorySystem memInternals addrType wordType where
  -- | General memory fetch. This always returns a 'wordType' value,
  -- even if the system's memory internals are byte addressable.
  mfetch :: addrType       -- ^ The address from which the instruction will be fetched
         -> memInternals   -- ^ The memory system
         -> wordType       -- ^ The fetched word

  -- | Fetch a block of words from the memory system
  mfetchN :: ( Unbox wordType
             ) =>
             addrType      -- ^ The address from which the instruction will be fetched
          -> Int           -- ^ Number of words to fetch
          -> memInternals  -- ^ The memory system
          -> Vector wordType -- ^ The words fetched

澄清:重点MemorySystem是允许指令解码器(例如,在 TRS-80 的情况下为 Z80 指令解码器)独立于其内存接口运行,这样解码器所需要做的就是调用mfetch下一条指令及其操作数。因此,memInternals是一种实现与部分由类型类函数指定的内存系统的接口的类型。

如何是最好的约束方式addrTypewordType同时允许memInterals多态,即允许memInternals表示不同的内存配置。我发现自己正在向看起来像这样的函数添加上下文:

foo :: ( MemorySystem memSys Word16 Word8) =>
    -> memSys
    -> Word16
    -> Word16
bar :: ( MemorySystem memSys Word16 Word8) =>
    -> memSys
    -> Word16
    -> Word8
bar mem pc = ...
  -- bar calls foo -> rigid type variable error...

这会导致 ghc 中出现很多刚性类型变量错误。

是否有更好的方法来表达 a MemorySystem,从而导致多态的“正确类型”,memInternals以便可以正确表示内存接口系列(并通过类型类或系列对其进行操作)?

4

2 回答 2

1

要扩展我的评论,您可以尝试使用关联的类型系列。但看起来您当前的用例由数据类型而不是类型类很好地服务:

{-# LANGUAGE TypeFamilies, RankNTypes #-}
import Data.Vector.Unboxed

class MemorySystem memInternals where
  type AddrType memInternals
  type WordType memInternals

  mfetch :: memInternals
         -> AddrType memInternals
         -> AddrType wordType

  mfetchN :: Unbox wordType
          => memInternals
          -> AddrType memInternals
          -> Int
          -> Vector wordType

-- The record type may be more flexible and appropriate here
data MemorySystem addrType wordType = MemorySystem {
    mfetch :: addrType -> wordType
  , mfetchN :: Unbox wordType => addrType -> Int -> Vector wordType
  }

看看类型类如何只有函数memInternals只接受一次类型参数并作为第一个参数?这似乎等同于记录方法。

于 2012-12-14T12:02:42.657 回答
0

The record approach does work best, at least for proper separation of concerns. Here's what works for me:

data (Unbox wordType) => MemorySystem addrType wordType memSys =
  MemorySystem
  { _mfetch :: addrType -> wordType
  , _ mfetchN :: addrType -> Int -> Vector wordType
  , _memInternals = memSys
  }

(Note that memInternals can be accessed from within mfetch and mfetchN, so no need to duplicate it as a parameter to those two functions.) It's then possible to create lenses for this record, e.g., using Control.Lens and makeLenses. It also permits defining types like:

import Data.Word
import Data.Int

type Z80addr = Word16
type Z80word = Word8
type Z80memory memConfig = MemorySystem Z80addr Z80word memConfig

that permit a polymorphic memory configuration. Many thanks to Chris for the suggestion -- I'm not sure why I got wrapped around the proverbial axle thinking in terms of type families.

于 2012-12-14T19:21:55.827 回答