开发者

Is there a way to do more "dynamic" data constructors in Haskell?

开发者 https://www.devze.com 2022-12-20 12:50 出处:网络
Is there some Haskell extension that enables the creation of more complex data constructors then GADT?

Is there some Haskell extension that enables the creation of more complex data constructors then GADT?

Suppose I wanted to create a data structure that is an ordered list, and have a data constructor similar to 开发者_如何学编程(:) that work with lists, with type signature:

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

But I want (>>>) to have a specific behavior, something like this:

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

So the structure will be always an ordered structure. (I don't now if this is a good practice, I'm just offering the simplest example that occurred me of the type of behavior I want).

Of course I can use a function (>>>), but then I'll have no pattern matching and other benefits I'd have it >>> was a data constructor.

Is there any way to do something like this?


You could make MyOrdList an abstract type and (>>>) a function and use view patterns. For simplicity, I use a standard list as a "backend" here.

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)

You can use it like that:

{-# LANGUAGE ViewPatterns #-}

import MyOrdList

ordlength :: MyOrdList a -> Int
ordlength (ordview -> OrdNil) = 0
ordlength (ordview -> OrdCons x xs) = 1 + ordlength xs

Works:

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

So your type is abstract, lists can only be constructed by emptyOrdList and (>>>) but you still have some pattern matching convenience.


You could make :>>> a data constructor, but you'd have to hide it to preserve your invariant. Notice that you can pattern-match against it as in 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

Then you might use the MyOrdList module as in

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)

Sample output:

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

精彩评论

暂无评论...
验证码 换一张
取 消