Giter VIP home page Giter VIP logo

in-other-words's People

Contributors

berberman avatar eddiemundo avatar indiscriminatecoding avatar kingofthehomeless avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

in-other-words's Issues

GHC 9.2.2?

I'm on a sabbatical, and I'm devoting some of that time to slamming myself face first into the bleeding edge of stuff that I think is neat in Haskell. I'm presently throwing myself at getting in-other-words building on 9.2.2 and then going on to write some libraries on top of it, so I mostly just want to use this ticket to track my findings and potentially get your input on how to fix things that are broken.

It'll take me a bit to get spun up on all the machinery here, so any pointers are highly valued. :)

Nits:

  • StarIsType warnings (easy fix)

Broken stuff:

Carrier instances

  • Control.Effect.Internal.Newtype.UnwrapTopC (looks like UnwrappedEff is hanging around too long, maybe?)
  • Control.Effect.Writer.ListenIntoEndoListenC: Could not deduce (Member (Tell (Endo o)) (Derivs m)) ... from the context: (Monoid o, HeadEffs '[Listen (Endo o), Tell (Endo o)] m)
  • Control.Effect.Writer.WriterIntoEndoWriterC: As above
  • Control.Effect.Union.UnionizeHeadC
    Could not deduce (Member (Union b) (Derivs m))
      arising from a use of ‘inj’
    from the context: (HeadEff (Union b) m, KnownList b)
    
    Could not deduce: Derivs m
                      ~ (_e0 : StripPrefix '[Union b] (Derivs m))
    from the context: (HeadEff (Union b) m, KnownList b)
    Expected: Union (Derivs m) z a
      Actual: Union (_e0 : StripPrefix '[Union b] (Derivs m)) z a
    

interpretViaHandler uses

I suspect these are all tied up in the Carrier instances, but I don't understand the machinery well enough yet to fix it.

  • Control.Effect.Trace.traceIntoTell: Could not deduce (Member (Tell String) (Derivs m)) ... from the context: HeadEff (Tell String) m
  • Control.Effect.Writer.tellIntoEndoTell: Could not deduce (Member (Tell (Endo o)) (Derivs m)) ... from the context: (Monoid o, HeadEff (Tell (Endo o)) m)
  • Control.Effect.Writer.listenIntoEndoListen: As above, plus Listen (Endo o)
  • Control.Effect.Writer.writerIntoEndoWriter: As above, plus Pass (Endo o)

Others

  • Control.Effect.Writer.tellIntoTell and Control.Effect.Writer.tellIntoTellSimple: Could not deduce (Member (Tell o') (Derivs m)), which I expect is just more of the carrier issue poking through.

First order simple usage example doesn't compile

I'm using GHC 8.8.4 and I get

[typecheck] [E] • Overlapping instances for Member
                              (State [i0]) '[State [String], Tell String, Throw [Char]]
    arising from a use of ‘get’
  Matching instances:
    instance forall a (e :: a) (r :: [a]) (_e :: a).
             Member e r =>
             Member e (_e : r)
      -- Defined in ‘Control.Effect.Internal.Membership’
    instance [overlapping] forall a (e :: a) (r :: [a]).
                           Member e (e : r)
      -- Defined in ‘Control.Effect.Internal.Membership’
  (The choice depends on the instantiation of ‘i0’
   To pick the first instance above, use IncoherentInstances
   when compiling the other instance declarations)
• In the first argument of ‘(>>=)’, namely ‘get’
  In a stmt of a 'do' block:
    get
      >>=
        \case
          [] -> throw "Inputs exhausted!"
          (x : xs) -> put xs >> return x
  In the first argument of ‘runAskActionSimple’, namely
    ‘(do get
           >>=
             \case
               [] -> throw "Inputs exhausted!"
               (x : xs) -> put xs >> return x)’

I have all the required language extensions in my cabal file, not sure if I need IncohrentInstances.

Add Tagged effect

Like polysemy's Tagged and fused-effects's Labelled. I've held off for three reasons:

  • I want it to work to together with Bundle, so users can use Eff (Tagged s (Error e)) constraints in code, and this raises a lot of questions about implementation.
  • I want to provide both a normal version -- like polysemy's Tagged -- and one function where there's a functional dependency from the label to the effect -- like fused-effects's Labelled. This also raises a lot of questions about implementation.
  • Effect newtypes are easy enough in in-other-words that the lack of Tagged isn't so bad.

Find more general primitive effects

It could be possible that there exists more general primitive effects that would encompass some that are currently used by the library without being more demanding to ThreadsEff.
Any such effect could be then added as a helper primitive effect, and the primitive effects it encompasses could then be specializations of the more general primitive effect.

Mask in particular strikes me as an effect for which a more general primitive effect exists. Perhaps the following:

data Bifunctorial s m a where
  Bifunctorially :: s a (m a) -> Bifunctorial s m a

(where s is constrained to be a Bifunctor)

or the following:

data BaseReify b m a where
  BaseReify :: (forall f. (forall x. m x -> b (f x)) -> b (f a))
            -> BaseReify b m a

(essentially a more restricted version of BaseControl.)

These are enough to implement Mask, and you can cook up monad transformers that can thread these but not BaseControl, but it's not clear if there are monad transformers that can thread Mask but not these.

expose internals of Control.Effect.Conc

What about exporting the constructor of Conc and/or

unliftConc :: Eff Conc m => ((forall x. m x -> IO x) -> IO a) -> m a

from e.g. Control.Effect.Internal.Conc in order to be able to use other methods in the style of Control.Concurrent.Async, e.g. from UnliftIO.Async?

a few questions that came up while implementing readline-in-other-words

First off: I like this effect system a lot, I think this is a very promising direction.

I have implemented readline-in-other-words for providing a haskeline compatible Readline effect utilizing the InputT monad transformer as a novel monad transformer, and was able to do everything reasonably well using the guides on the wiki, but have a few lingering questions:

  • I don't understand why there is a Carrier m constraint on most interpreters even when it isn't strictly necessary. I was able to compile
    main :: IO ()
    main = runM $
      runReadline defaultSettings $
        handleInterrupt (outputStrLn "Interrupt!" *> repl) $
          withInterrupt $ do
            mline <- getInputLine "> "
            case mline of
              Nothing -> pure ()
              Just line -> outputStrLn line *> repl
    
    even when runReadline didn't have any of the threading/Carrier constraints. I think I will probably add them for consistency, but it feels like a mistake to have extraneous constraints unless they are needed for type safety in a way that I don't understand.
  • BaseControl's constructor isn't exported except through internal modules. This was an obstacle when trying to write a ThreadsEff InputT (BaseControl b) instance because InputT doesn't conform to MonadBaseControl thus I couldn't use threadBaseControlViaClass but it still admits an implementation via withRunInBase. This ended up not being a problem because I implemented the threading constraints on ReadlineC anyways, and then just implemented a MonadBaseControl instance for ReadlineC, but it might be nice to expose it anyways for similar circumstances if that would be possible
  • I constrain the base monad with MonadIO m in
    runReadline ::
      (MonadIO m, MonadMask m) =>
      H.Settings m ->
      ReadlineInterruptC m a ->
      m a
    runReadline settings = H.runInputT settings . unReadlineInterruptC
    
    because runInputT has it as a constraint. Similarly I require that constraint for my Carrier instance because most of the actions require it. I don't see any instances where MonadIO constrains the base monad the main library, instead it seems to use Eff (Embed IO) m when it needs to accomplish something similar. I don't see how to perform this conversion. If that is possible, is a similar conversion possible for MonadMask?
  • I ended up implementing ThreadsEff instances on one of my carrier newtypes ReadlineC instead of InputT directly to avoid orphan instances. Its still ergonomic because I can coerce between the two different carriers, but doesn't follow the main pattern which is for the threading constraints to be defined directly on the monad transformers. Do you have an opinion on this? In general it seems that libraries outside of the main one will always have this problem because the only way orphans are avoided in the main library is by defining them in the same module as the class.
  • Naming: I kind of hope packages for this library adopt the *-in-other-words naming scheme rather than the traditional in-other-words-* naming scheme for integration package (e.g. polysemy-readline) because it reads better in English grammar

Oof, this ended up a lot longer than I initially expected! If you made it this far, I'll add that I would appreciate it if you reviewed my library before I publish it on hackage: https://github.com/lehmacdj/readline-in-other-words/blob/main/src/Control/Effect/Readline/Internal.hs

Thanks!

question: why is interpretSimple less performant than interpret

The documentation says that interpret is more performant than interpretSimple. But It seems that the only difference is that interpret uses reify/reflect to pass the effect handler where interpretSimple uses a ReaderT monad. My question is how exactly does this make a difference? I have a vague feeling that this has something to do with the typeclass machinery but I'm not very sure.

Remove ContT's ThreadsEff instance for ReaderPrim?

The traditional ContT -- which is used for runContFast and runShiftFast -- has always had problems lifting higher-order effects. It currently only threads ReaderPrim, building on the MonadReader instance that ContT has. Now, it turns out that the MonadReader instance has bad semantics. Because of that, I'm considering removing the ThreadsEff instance ContT has for ReaderPrim, making it so that ContFastThreads accepts no primitive effects whatsoever.

Add an interpreter that does not use OpaqueExc for Error effect

Since the Error effect can be interpreted as exceptions, one reasonable use case of Error is to mark codes that might throw exceptions, such as readFile:

readFile' :: (Eff (Embed IO) m, Eff (Error IOException) m) => FilePath -> m String
readFile' = embed . readFile

test :: IO (Either IOException String)
test = runM $ errorToIO $ readFile "testfile"

But because errorToErrorIO throws and catches the wrapper OpaqueExc instead of the exception itself, the underlying exception IOException won't actually get handled as one might want it to. I feel like there should be an interpreter that operates on the exception itself instead of OpaqueExc. Also, I'd like to argue that interpreting two identical Error e effect isn't really a common use case.

Quality of error messages

This is a big one. It's possible that with enough type-level hackery, most of the many obtuse error messages detailed on the wiki can be made less obtuse. This warrants a lot of research and experimentation. At the moment, I don't know the best approach.

Investigate changing constructor of `BaseControl` to lessen restrictions on `threadBaseControlViaUnlift`

threadBaseControlViaUnlift requires that the relevant transformer is representational in the transformed monad,which is not satisfied by abstract monad transformers. A relevant use-case for removing this restriction is for haskeline's InputT, as shown in #14.

Two plausible approaches to solve this:

  1. Make BaseControl MonadTransControl-based rather than MonadBaseControl-based
  2. Inline the entire MonadBaseControl dictionary into BaseControl.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.