module Platform.DoAnything ( Handler (Handler), ) where -- | -- tldr; Having a value of this type represents an allowance to run any @IO@ in -- the @Task@ type. Intended for use in effects packages only. -- -- Longer explanation: -- -- One of our goals with creating a shared @Task@ type is that our libraries -- can all use it. Instead of returning polymorphic values like: -- -- (MonadError e m, Monad IO m) => m a -- -- We could instead let our libraries return @Task e m@. Looks simpler, no? -- -- But for this to fly our libraries will need to be able to run @IO@ in -- @Task@. The postgres library for example will want to talk to the -- database. How should it take a @IO a@ returned from @postgresql-typed@ -- and turn it into an @Task e a@ to return to the library user? -- -- @MonadIO@ is intended for this stuff. By deriving it for our @Task@ type -- we could use @liftIO@ to take an @IO a@ value and turn it into @Task e a@. -- But any part of the code could do this, and we'd like to restrict -- ourselves from running IO in our applications to functions that take a -- handler, indicating the type of effect they perform. -- -- This commit uses the handler pattern to restrict access to arbitrary IO -- in @Task@: You can turn an @IO a@ into a @Task e a@, but only if you can -- submit a @Handler@ to proof you're allowed. Are libraries will -- be allowed, but our applications not. -- -- You can only get a handler wrapped in an @IO@. This means you -- can only use it in an environment where you can perform arbitrary IO. We -- cannot extract the value from a @Data.Acquire@ in our @Task@ code, but we can -- use it to generate handlers for other packages. The Postgres package for -- example can store the @Handler@ in its @Connection@ handler, and -- so whenever Postgres is passed a @Connection@ to perform a query, it also -- has access to the @Handler@ necessary to perform IO. data Handler = Handler