代码之家  ›  专栏  ›  技术社区  ›  Rafael S. Calsaverini

有没有办法在haskell中做更多的“动态”数据构造器?

  •  4
  • Rafael S. Calsaverini  · 技术社区  · 15 年前

    是否有一些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;) ,但我将没有模式匹配和其他好处,我会有它的 >>> 是数据构造函数。

    有什么办法可以做这种事吗?

    2 回复  |  直到 15 年前
        1
  •  3
  •   Greg Bacon    15 年前

    你可以做 :>>> 一个数据构造函数,但是为了保持不变量,必须隐藏它。注意,您可以根据它进行模式匹配,如 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 -> <empty>
        2
  •  6
  •   Rüdiger Hanke shraddha hattimare    15 年前

    你可以做 MyOrdList 抽象类型和 (>>>) 一个函数并使用视图模式。为了简单起见,我在这里使用标准列表作为“后端”。

    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;) 但是您仍然有一些模式匹配的便利性。