【问题标题】:Is there a way to do more "dynamic" data constructors in Haskell?有没有办法在 Haskell 中做更多的“动态”数据构造函数?
【发布时间】:2010-02-12 16:08:35
【问题描述】:

是否有一些 Haskell 扩展可以创建比 GADT 更复杂的数据构造函数?

假设我想创建一个有序列表的数据结构,并有一个类似于(:) 的数据构造函数,它可以处理列表,带有类型签名:

data MyOrdList a where
    (>>>) :: (Ord a) -> a -> MyOrdList a -> MyOrdList a

但我希望(>>>) 具有特定的行为,如下所示:

(>>>) :: (Ord a) => a -> [a] -> [a]
x >>> [] = [x] 
x >>> xs = low ++ [x] ++ high 
  where low  = filter (<x) xs
      high = filter (>x) xs

因此结构将始终是有序结构。 (如果这是一个好习惯,我现在不知道,我只是提供我想要的行为类型发生的最简单的例子)。

当然我可以使用函数(&gt;&gt;&gt;),但是我将没有模式匹配和其他好处我会拥有它&gt;&gt;&gt;是一个数据构造函数。

有没有办法做这样的事情?

【问题讨论】:

    标签: data-structures haskell constructor


    【解决方案1】:

    您可以将MyOrdList 设为抽象类型,将(&gt;&gt;&gt;) 设为函数并使用视图模式。为简单起见,我在这里使用标准列表作为“后端”。

    module MyOrdList
      (MyOrdList,
       MyOrdListView (OrdNil, OrdCons),
       (>>>),
       emptyOrdList,
       ordview
      ) where
    
    import Data.List (sort)
    
    newtype MyOrdList a = List [a]
      deriving Show
    
    data MyOrdListView a = OrdNil | OrdCons a (MyOrdList a)
    
    infixr 5 >>>
    
    (>>>) :: (Ord a) => a -> MyOrdList a -> MyOrdList a
    x >>> (List xs) = List (sort $ x:xs)
    
    emptyOrdList = List []
    
    ordview :: MyOrdList a -> MyOrdListView a
    ordview (List []) = OrdNil
    ordview (List (x:xs)) = OrdCons x (List xs)
    

    你可以这样使用它:

    {-# LANGUAGE ViewPatterns #-}
    
    import MyOrdList
    
    ordlength :: MyOrdList a -> Int
    ordlength (ordview -> OrdNil) = 0
    ordlength (ordview -> OrdCons x xs) = 1 + ordlength xs
    

    作品:

    *Main> ordlength $ 2 >>> 3 >>> 1 >>> emptyOrdList 
    3
    *Main> 2 >>> 3 >>> 1 >>> emptyOrdList 
    List [1,2,3]
    

    所以你的类型是抽象的,列表只能由emptyOrdList(&gt;&gt;&gt;) 构造,但你仍然有一些模式匹配的便利。

    【讨论】:

      【解决方案2】:

      您可以将:&gt;&gt;&gt; 设为数据构造函数,但您必须隐藏它以保留您的不变量。请注意,您可以像在render 中一样对其进行模式匹配:

      module MyOrdList (mkMyOrdList,MyOrdList,render) where
      
      import Data.List
      
      import qualified Data.ByteString as BS
      
      data MyOrdList a
        = EmptyDataList
        | (:>>>) a (MyOrdList a)
        deriving (Show)
      
      mkMyOrdList [] = EmptyDataList
      mkMyOrdList xs = y :>>> mkMyOrdList ys
        where y = minimum xs
              ys = delete y xs 
      
      render :: (Show a) => MyOrdList a -> String
      render EmptyDataList = "<empty>"
      render (x :>>> xs) = (show x) ++ " -> " ++ render xs
      

      那么你可以使用MyOrdList 模块

      module Main where
      
      import Control.Applicative
      import System.IO
      
      import qualified Data.ByteString as BS
      
      import MyOrdList
      
      main = do
        h <- openBinaryFile "/dev/urandom" ReadMode 
        cs <- readBytes 10 h
        -- but you cannot write...
        -- let bad = 3 :>>> 2 :>>> 1 :>>> EmptyOrdList
        putStrLn (render $ mkMyOrdList cs)
        where
          readBytes 0 _ = return []
          readBytes n h = do c <- BS.head <$> BS.hGet h 1 
                             cs <- readBytes (n-1) h
                             return (c:cs)
      

      样本输出:

      54 -> 57 -> 64 -> 98 -> 131 -> 146 -> 147 -> 148 -> 190 -> 250 -> 

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-11
        • 1970-01-01
        相关资源
        最近更新 更多