多态返回类型



函数从字节流中读取 ID。它知道id的大小 - 可以是4或8个字节。如何使返回类型多态?

(伪代码:(

    class (Integral a) => IdSize a where
      size :: a -> Int
    instance IdSize Int32 ...
    instance IdSize Int64 ...
    data Data = Data (Map (IdSize a) String)
    readData :: Data (Map (IdSize a) String)
    readId :: (forall a. IdSize a) => a -- kind of this, but not right

此 readId 需要来自调用方的实例 IdSize,但调用方不知道大小。同样,readData 返回的 Map 需要是多态的,但调用方不知道实际类型。使用映射的函数将知道该类型。

如果某物只能有两种类型,它就不是"多态的"。这只是两种类型的不相交并集,或"总和"类型。

import Data.Int
data Idx = I32 Int32
         | I64 Int64
  deriving (Show)
readId 4 _ = I32 0x12345678
readId _ _ = I64 0x1234567812345678
idSize (I32 _) = 4
idSize _       = 8
main :: IO ()
main = do
  let input = () -- this would be your input stream
  let idx1 = readId 4 input
  let idx2 = readId 8 input
  putStrLn $ "idx 1: size " ++ (show $ idSize idx1) ++ " value: " ++ (show idx1)
  putStrLn $ "idx 2: size " ++ (show $ idSize idx2) ++ " value: " ++ (show idx2)
  return ()
当您

确实需要在数据类型的类型签名方面具有更大的灵活性时,例如当您构建要约束为类型良好的构造的抽象语法树时,GADT 是一个很好的方法:http://en.wikibooks.org/wiki/Haskell/GADT

下面是 GADT 的示例:

{-# LANGUAGE GADTs              #-}
{-# LANGUAGE StandaloneDeriving #-}
import           Data.Int
data Idx a where
  I32 :: Int32 -> Idx Int32
  I64 :: Int64 -> Idx Int64
deriving instance Show (Idx a)
readId32 :: t -> Idx Int32
readId32 _ = I32 0x12345678
readId64 :: t -> Idx Int64
readId64 _ = I64 0x1234567812345678
idSize :: Num a => Idx t -> a
idSize (I32 _) = 4
idSize _       = 8
main :: IO ()
main = do
  let idx1 = readId32 ()
  let idx2 = readId64 ()
  putStrLn $ "idx 1: size " ++ (show $ idSize idx1) ++ " value: " ++ (show idx1)
  putStrLn $ "idx 2: size " ++ (show $ idSize idx2) ++ " value: " ++ (show idx2)
  return ()

我不确定这是否是你所追求的,但它确实可以让您专门化类型,以便您不能将 Idx Int32 s 与 Idx Int64 s 混合,但您仍然可以编写多态Idx a函数,如 idSize .

啊,好的,我们可以使用其他包装器类型来解决其中的一些问题:

        {-# LANGUAGE RankNTypes, ConstraintKinds, ExistentialQuantification #-}
        import Data.Int
        data Idx = forall a. IdSize a => Idx a
        instance Show Idx where
          show (Idx a) = show a
        class (Integral a, Show a) => IdSize a where
          size :: a -> Int
        instance IdSize Int32 where
          size _ = 4
        instance IdSize Int64 where
          size _ = 8
        readId :: Int -> Idx
        readId 4 = Idx (4 :: Int32)
        readId _ = Idx (8 :: Int64)
        main = print $ readId 8

然后也许数据将保存一个映射 ID 字符串。

以下内容可以满足我的要求:

    {-# LANGUAGE RankNTypes, ExistentialQuantification #-}
    import Data.Int
    import Data.Typeable
    import qualified Data.Map as M
    data H = forall a. IdSize a => H a (M.Map a String)
    class (Integral a, Show a, Typeable a) => IdSize a where
      size :: a -> Int
      readId :: a
    instance IdSize Int32 where
      size _ = 4
      readId = 4
    instance IdSize Int64 where
      size _ = 8
      readId = 8
    use :: (forall a. IdSize a => a -> M.Map a String -> b) -> H -> b
    use f (H i m) = f i m
    idSize :: H -> Int
    idSize (H i _) = size i
    mkH :: Int -> H
    mkH 4 = H (4 :: Int32) (M.singleton (4 :: Int32) "int32")
    mkH _ = H (8 :: Int64) (M.singleton (8 :: Int64) "int64")
    main = print $ use (M.lookup . const readId) $ mkH 4

mkH 可用于构造一个 H,它对调用方是不透明的。然后,调用方可以传递一个要使用的函数,该函数将解构 H 并调用给定的函数。该函数必须是 RankN 多态的 - 它应该适用于任何 IdSize 实例。这是隐藏 IdSize 实现的设计意图。

最新更新