Statically applying two objects created from the same (Int) "seed"

In the library I'm working on, I have an API similar to the following:

data Collection a = Collection Seed {-etc...-}
type Seed = Int

newCollection :: Seed -> IO (Collection a)
newCollection = undefined

insert :: a -> Collection a -> IO () -- ...and other mutable set-like functions
insert = undefined

mergeCollections :: Collection a -> Collection a -> IO (Collection a)
mergeCollections (Collection s0 {-etc...-}) (Collection s1 {-etc...-}) 
  | s0 /= s1  = error "This is invalid; how can we make it statically unreachable?"
  | otherwise = undefined

I would like to ensure that the user cannot invoke mergeCollectionson Collectioncreated with different values Seed.

I was thinking about trying to mark with a Collectionnatural type level: I think it would mean that at compile time SeedI would need to know statically, but my users can get it from an environment variable or user input, so I don't think it worked.

I also hoped that I could do something like:

newtype Seed u = Seed Int
newSeed :: Int -> Seed u
newCollection :: Seed u -> IO (Collection u a)
mergeCollections :: Collection u a -> Collection u a -> IO (Collection u a)

- a Seed - , , merge , newSeed. (-) a b - : let a = newSeed 1; b = newSeed 1;.

?

, , Seed Collection s. (, ..) , IO :

  • Collection (), , - , .

  • , vars ( ):

    main = do
       s1 <- getEnv "SEED1"
       s2 <- getEnv "SEED2"
       -- ... many Collections may be created dynamically from these seeds
       -- and dynamically merged later
    
+4
2

, . , , ; , . :

merge :: Collection a -> Collection a -> IO (Maybe (Collection a))

, , "", , - , ST: , "" , , , , phantom, phantom. ( .) :

{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Collection (Collection, COp, newCollection, merge, inspect, runCOp) where

import Control.Monad.Reader

type Seed = Int
data Collection s a = Collection Seed
newtype COp s a = COp (Seed -> a) deriving (Functor, Applicative, Monad, MonadReader Seed)

newCollection :: COp s (Collection s a)
newCollection = Collection <$> ask

merge :: Collection s a -> Collection s a -> COp s (Collection s a)
merge l r = return (whatever l r) where
  whatever = const

-- just an example; substitute whatever functions you want to have for
-- consuming Collections
inspect :: Collection s a -> COp s Int
inspect (Collection seed) = return seed

runCOp :: (forall s. COp s a) -> Seed -> a
runCOp (COp f) = f

, COp Collection . , , Collection COp; runCOp newCollection ( , "" , ). Collection, , merge, .

+7

, , , . typechecker , , compiletime. , , typechecker , , , typechecker - . , , - - ExceptT, .

+1

Source: https://habr.com/ru/post/1651123/


All Articles