Experimental IRC log happs-2008-02-28

Available formats: content-negotiated html turtle (see SIOC for the vocabulary)

Back to channel and daily index: content-negotiated html turtle

These logs are provided as an experiment in indexing discussions using IRCHub.py, Irc2RDF.hs, and SIOC.

00:02:00<Saizan>but i can't use the return type e.g. Query (State param) Int, to thread the "param" to the component system?
00:03:29<mightybyte>I'm not sure what you mean.
00:07:24<Saizan>in AllIn, numSessions:: Proxy (Session val) -> Query (Session val) Int, so you can use query (NumSessions (proxy :: Session Int)) and query (NumSession (proxy :: Session Foo)) and they'll work on different part of the state
00:08:56<mightybyte>Ahhh, so the Proxy is only necessary because Session takes a type parameter.
00:09:35<Saizan>afaiu, yeah
00:10:20<Saizan>but i'm wondering why the presence of the parameter in the return type is not enough
00:11:04<mightybyte>Yeah, seems like it should be.
00:12:22<Saizan>Lemmih: is there a short explaination or i should read HAppS-State's code?
00:19:30<mightybyte>Ok, one more question. My addUser fix could look like "addUser name u = do exists <- isUser name; unless (not exists) $ modUsers $ M.insert name u".
00:19:40<mightybyte>If I do that, then the type signature is this...
00:20:14<mightybyte>addUser :: (MonadReader State m, MonadState State m) => String -> User -> m ()
00:21:08<mightybyte>Will that work as "update $ AddUser", or do I need to get rid of the MonadReader port?
00:21:12<mightybyte>part
00:24:18<mightybyte>It compiles fine, I just wasn't sure if it's ok to have both the Reader and State monads in one function.
00:25:22<Saizan>it's ok. there's a MonadReader instance for Update
00:26:21<mightybyte>Great. That way I don't have to make another version of isUser in the state monad.
00:27:13<mightybyte>While I'm on that topic, I have a question about how to talk about monads.
00:27:30<mightybyte>How would I say in English what addUser or isUser is?
00:27:59<mightybyte>Do I say "isUser returns a computation in the Reader monad"?
00:29:11<mightybyte>In writing my posts, I've had trouble knowing how to properly talk about code that involves monads.
00:30:00<Saizan>it's not exaclty in the Reader monad, if by Reader we mean mtl's one
00:30:22<Saizan>and we usually use action instead of computation in these cases
00:30:44<mightybyte>Hmmm, what's the difference?
00:31:33<Saizan>well Reader is specifically defined as newtype Reader r a = Reader {runReader :: (r -> a) }
00:32:39<Saizan>while what we're using in HAppS is a more complex monad that also is an instance of MonadReader
00:33:00<mightybyte>Yeah, I'm interested in both the HAppS case and the general case.
00:33:44<mightybyte>If "foo :: Monad m => m ()", how would I talk about foo?
00:34:04<dbpatterson>I keep getting this error - No instance for (Control.Monad.Trans.MonadIO []) arising from a use of `update' at Main.hs:82:39-44 - which I was under the impression usually came up when I hadnt used mkMethods properly
00:34:14<mightybyte>Maybe "foo returns a Monad action" (or computation)?
00:34:35<mightybyte>dbpatterson: Is this in my code?
00:34:37<Saizan>mightybyte: foo _is_ a monadic action
00:34:38<dbpatterson>but my code is - deleteUserPage (Just sid) = [anyRequest $ do ses <- query $ (GetSession $ sid) ; ok $ toResponse $ deleteUser ses]
00:34:44<dbpatterson>mightybyte: kind of.. I'm adding to it
00:34:55<dbpatterson>where the actual error is in the helper, deleteUser
00:35:09<dbpatterson>which is deleteUser (Just (SessionData s)) = do update $ DelUser s; "deleted"
00:35:37<Saizan>you can't juse put "deleted" by itself there
00:35:42<mightybyte>Saizan: Ok, but it's also critical (and I think I sometimes get this wrong) that foo does NOT *run* a monadic action, correct?
00:36:40<dbpatterson>Saizan: hmm.. well putting in return "deleted" creates a series of other errors.. which I thought meant it was 'a bad thing'
00:37:29<Saizan>dbpatterson: because toResponse expects an instance of ToMessage
00:37:35<Saizan>mightybyte: right
00:37:59<mightybyte>Saizan: So is the word "action" preferred to "computation"?
00:38:53<Saizan>deleteUser (Just (SessionData s)) = do update $ DelUser s; return "deleted"; deleteUserPage (Just sid) = [anyRequest $ do ses <- query $ (GetSession $ sid) ; msg <- deleteUser ses; ok $ toResponse msg]
00:38:59<Saizan>mightybyte: it's more common
00:39:09<mightybyte>Ok.
00:40:27<Saizan>btw, there's no intrinsic difference from what we call action and any other value.. but action is usually a good methaphor for a value with monadic type
00:41:27<mightybyte>So in my Main.hs, I have "loginPage :: (MonadIO m) => UserAuthInfo -> [ServerPartT m Response]".
00:41:47<mightybyte>So I would say something like "loginPage is a list of ServerPartT actions"?
00:42:20<mightybyte>Or would it be slightly different since ServerPartT is a monad transformer?
00:42:24<Saizan>well, the monad there is (ServerPartT m)
00:42:27<dbpatterson>Saizan: okay, that works, but in order to get the Nothing case of deleteUser to properly type I need it to be deleteUser Nothing = do return "nothing deleted" - why?
00:43:08<Saizan>dbpatterson: because both cases must have the same type
00:43:37<dbpatterson>Saizan: yeah, no, I realized that, but what exactly is the type of 'return String" vs "String"?
00:43:45<Saizan>dbpatterson: in the Just case you had to use return because you used update which is monadic
00:43:49<dbpatterson>I understand they are different
00:44:14<Saizan>"foo" :: String, return "foo" :: Monad m => m String
00:44:38<dbpatterson>okay, thanks
00:44:44<Saizan>in this case m = ServerPartT IO, if i'm right
00:45:13<dbpatterson>mightybyte: if I clean it up, do you want your tutorial + the ability to delete users and restrict the total number of users?
00:45:20<dbpatterson>and logout
00:45:26<dbpatterson>(ie, delete sessions)
00:45:31<mightybyte>dbpatterson: Sure!
00:46:16<mightybyte>dbpatterson: If you're interested, it also might be good to merge the ETime stuff from AllIn, so that it's a more useful framework for the real world.
00:46:38<mightybyte>dbpatterson: I thought about doing that in the future.
00:46:40<dbpatterson>that involves timing out sessions?
00:46:52<mightybyte>dbpatterson: Yeah
00:47:04<mightybyte>Saizan: So what is a ServerPartT?
00:47:44<mightybyte>Would we say "It's a transformer that transforms a ServerPart action into an IO action"?
00:47:46<dbpatterson>mightybyte: I might in the future... for now though explicit logging out is okay (coding mostly for myself...)
00:47:59<mightybyte>dbpatterson: Ok.
00:48:28<Saizan>mightybyte: well, there are two things named ServerPartT
00:48:41<mightybyte>dbpatterson: If you add it as an annotation to the hpaste, then it will be visible to everyone automatically.
00:48:50<dbpatterson>yup, doing it currrently
00:48:56<mightybyte>Great
00:50:15<porrifolius>mightybyte: Regarding your tutorial, it's probably worth mentioning that everything passed to a update is written to disk. So your passwords will be stored in plaintext on the disk regardless of what you do 'inside'. For a production system you'd need to salt and hash before doing the update... which raises other complexities of course.
00:50:29<Saizan>mightybyte: ServerPartT as a type constructor, which is an instance of MonadTrans and so a monadtransformer, which means that forall m if Monad m then Monad (ServerPartT m)
00:50:52<mightybyte>porrifolius: Yeah, I thought I mentioned that briefly. (Maybe I just thought about mentioning it.)
00:51:24<Saizan>mightybyte: and then you've lift :: Monad m => m a -> ServerPartT m a, which is what transforms actions.
00:51:25<porrifolius>mightybyte: Ok, you might have. I just realised it myself so thought I'd mention it to you.
00:51:54<mightybyte>porrifolius: Ok, thanks. I'm working on a post to correct the atomicity problem in checkAndAdd.
00:52:21<dbpatterson>mightybyte: I posted to hpaste the changes to Session.hs - I'll post the 'interface' in a second
00:52:42<dbpatterson>(ie, changes to Main.hs, so you can actually use the stuff)
00:52:49<mightybyte>Saizan: How is lift used?
00:54:19<Saizan>mightybyte: well, suppose we're working in ServerPartT IO, if i want to use putStr i need to wrap it in lift, like do ..; lift (putStr "foo"); ..
00:56:27<Saizan>mightybyte: i.e. whenever you want to use an action of the "internal" monad you use lift to promote it
00:57:05<mightybyte>Saizan: Ok, so that just allows you to print to the screen like you would in the IO monad when you're in ServerPartT IO instead?
00:57:15<dbpatterson>oh hmm... how do you delete a cookie?
00:57:16<Saizan>yeah
00:57:34<dbpatterson>does delCookie "sid" work?
00:58:20<Saizan>i don't see a delCookie
00:59:08<dbpatterson>rmCookie? is there haddock stuff for this, or are you just grepping code?
00:59:15<mightybyte>delSesCookie
00:59:26<mightybyte>I'm reading HAppS-HTTP/src/HAppS/Server/Cookie.hs
01:00:03<mightybyte>Oh wait, delSesCookie is commented out.
01:00:04<dbpatterson>that is commented out(?)
01:00:11<Saizan>you can generate the haddock docs with runghc Setup.hs haddock
01:00:20<mightybyte>I don't have the 0.9.2 release yet either.
01:00:43<Saizan>ACTION untar HAppS-Server
01:03:21<dbpatterson>okay well I put it up because I didnt want hpaste's session to time out... so for now it works, but it leaves cookies in the browser (which is bad form)
01:05:39<mightybyte>Saizan: So the new signature for my addUser "addUser :: (MonadReader State m, MonadState State m) => String -> User -> m ()" says that addUser is either a Reader action or a State action?
01:05:50<dbpatterson>it looks like (in src/Server/SimpleHTTP.hs) that delCookie has not yet been created :)
01:07:18<mightybyte>Saizan: Would you say "either" or "both"? (I know I'm being picky, but I really want to have clear correct English descriptions.)
01:07:51<Saizan>dbpatterson: i think you can just use addCookie with 0 as seconds
01:08:02<Saizan>mightybyte: both
01:08:44<Saizan>mightybyte: but saying Reader and State can be confusing, since those are the names of two specific monads.
01:08:50<mightybyte>Saizan: So at runtime, would the actual type of each invocation still be both, or would it change depending on what happened?
01:09:26<Saizan>mightybyte: while here you only mean that the monad you're using must be both an instance of MonadReader and MonadState
01:09:49<mightybyte>Ahhhh
01:10:19<Saizan>:)
01:11:13<mightybyte>So it seems that the declaration says the user must have both instances, but each invocation could be either?
01:12:52<Saizan>user?
01:13:23<mightybyte>...from your "the monad you're using"
01:14:22<Saizan>each invocation must use a type for "m" which is both an instance of MonadReader State and MonadState State
01:14:31<mightybyte>Although with my particular implementation (do e <- isUser name; unless (not exists) $ modUsers...) I guess a successful add would use both the Reader and the State instances. How would the compiler do that and still maintain the transactional integritiy?
01:14:53<mightybyte>Ok
01:16:11<Saizan>at runtime instances are just dictionaries of methods, when they're not compiled away
01:17:41<Saizan>you can imagine addUser taking those dictionaries as two more parameters
01:18:06<Saizan>which get passed from the context of invocation
01:18:41<mightybyte>Ok, but how does it manage both monads when as I understand it each monad has its own rules for sequencing computations?
01:19:02<Saizan>the point is that you're not using 2 monads
01:19:24<Saizan>you're going to use only one monad, but that monad will be both a MonadReader and a MonadState
01:19:46<Saizan>do you know the difference between a type and a typeclass, right?
01:20:00<mightybyte>Vaguely
01:20:31<mightybyte>I think of a typeclass kind of like a Java interface.
01:20:43<Saizan>yes that's a good start
01:21:09<Saizan>and a single class in java can implement as many interfaces it wish, no?
01:21:19<mightybyte>And types would be like objects. If they are an instance of the typeclass, then they have to implement the right functions.
01:21:23<mightybyte>Sure
01:21:27<Saizan>no
01:21:32<Saizan>types are not objects
01:21:52<mightybyte>Ok, then like classes?
01:22:10<Saizan>yeah, classes without methods and inheritance
01:22:23<mightybyte>Ok
01:22:39<Saizan>so essentially a type is a set of values
01:22:51<mightybyte>Ahhh, I think I'm seeing.
01:23:38<mightybyte>So mkMethods sets the transaction boundaries at the beginning and end of the function.
01:23:43<Saizan>and a typeclass is like a set of types, but in this case a type can be a member of many typeclasses
01:24:04<mightybyte>Then (in addUser), when it gets to isUser, it calls on the code from the reader monad which reads the state.
01:24:39<mightybyte>...and when it gets to modUsers, it calls on the code from the State monad which just so happens to modify the same state that was read by the Reader monad code.
01:24:51<Saizan>what do you mean by "reader monad" ?
01:25:18<Saizan>the "m" must be the same in all the do block
01:25:35<mightybyte>Well, the code for isUser which occurs in the reader monad because of its type.
01:26:11<Saizan>it doesn't occur in the reader monad
01:26:17<Saizan>it occurs in a monad m, which must be an instance of the class MonadReader
01:26:29<Saizan>m is not better specified yet.
01:26:45<mightybyte>Ok. When does m become better specified?
01:26:53<dbpatter1on>mightybyte: cookie stuff is set, Saizan's idea about just replacing it with one that would time out was perfect
01:27:18<mightybyte>dbpatter1on: Ok, that's great.
01:27:53<dbpatter1on>also, I didnt put it in because it is changing yours, but setting limits on numbers of users is obviously just putting in more tests in checkAndAdd
01:28:14<mightybyte>Sure
01:28:21<Saizan>mightybyte: well in addUser we add another class constraint on it, that it must be also an instance of MonadState, afaiu m gets fixed to a concrete type only in mkMethods, and it will be (Update State)
01:29:06<Saizan>maybe a simple example will clarify
01:29:17<mightybyte>Saizan: Oh, interesting. I was thinking that it wouldn't get fixed until runtime...when you would know.
01:29:50<mightybyte>...but it makes sense to get fixed in mkMethods.
01:29:56<Saizan>there's not much to know
01:30:23<Saizan>have you seen how the mtl's State monad is implemented?
01:30:49<mightybyte>Not sure. I've perused some of the code briefly.
01:32:01<Saizan>but you know that it provides get and put, right?
01:32:31<mightybyte>Yes
01:33:36<Saizan>nothing stops me to write instance MonadReader st (State st) where ask = get; foo :: MonadReader Int m => m Int; foo = ask; foo' :: State Int Int; foo' = foo
01:34:18<mightybyte>Yeah, that's what I was wondering...where the ask-get translation takes place.
01:34:20<Saizan>so i can use foo with the State monad even if it uses MonadReader features
01:34:56<Saizan>in HAppS is similar, but the monad is still different
01:35:24<Saizan>type Update state = Ev (Control.Monad.State.Lazy.StateT state GHC.Conc.STM)
01:35:41<mightybyte>Does HAppS provide that translation?
01:36:09<Saizan>yes, it provides a MonadReader instance for Update
01:36:21<mightybyte>Ok
01:37:05<Saizan>the point is that you're not using different monads, but different interfaces for the same one, as you can mix different monads in the same do-block
01:37:14<Saizan>err, "can't"
01:37:47<mightybyte>It actually seems that in some sense, it's both "can" and "can't". :)
01:38:49<mightybyte>So the type system ensures that these "multiple monad" situations can only happen when some code somewhere along the way makes the translation?
01:39:15<Saizan>it's not necessarily a translation.
01:39:36<mightybyte>Well, a mapping of the methods of one monad into methods of another?
01:39:40<Saizan>but it ensures that the type provides all the required interfaces
01:41:16<mightybyte>And it just so happens that HAppS uses the State monad as the underlying implementation and provides a reader-like interface to the state monad when necessary?
01:41:59<Saizan>it's something more than the plain State monad..
01:42:34<Saizan>when we talk about a monad we refer to a type, rather than a typeclass
01:42:42<mightybyte>Ok, but thanks to the power of the monad abstraction, those details don't matter to me if I'm just using HAppS.
01:43:15<mightybyte>But isn't Monad a typeclass?
01:43:39<Saizan>Monad is a typeclass, so members of it are monads :)
01:44:11<Saizan>is like saying that NaturalNumber is a set, and 0 is a natural number, because it is a member of that set
01:44:18<mightybyte>Ahh, and when we talk about a monad, we're always eventually referring to a specific type.
01:44:57<Saizan>yeah
01:45:37<Saizan>that's also because not having subtyping the concrete type will show up in the end. (modulo existential types)
01:45:56<mightybyte>So HAppS *could* use a totally different monad under the hood to implement updates. All the user cares about is that it provides a reader and state interface to whatever is under the hood.
01:46:21<Saizan>exactly
01:46:56<mightybyte>Which was the flaw in my original thinking that the compiler was somehow doing both of those monads (themselves concrete types).
01:49:19<mightybyte>I'm liking monads more every day. It was cool a few weeks ago when the full understanding of how they can invisibly thread all kinds of information and still be perfectly typesafe.
01:49:33<mightybyte>Just gorgeous.
01:50:38<Saizan>yeah, they're powerful :)
01:51:18<mightybyte>And that description really applies to only the State monad. The real abstraction is much more powerful.
01:51:44<mightybyte>It just takes awhile to grasp all the capabilities that monads afford.
01:52:40<Saizan>in general i see them as a nice way to abstract repetitive patterns in the code
01:53:10<mightybyte>Which is arguably one of the most important tasks of the programmer.
01:55:09<Saizan>http://www.haskell.org/all_about_monads/html/ <-- part II briefly introduces the common ones
01:55:32<mightybyte>Yeah, I've looked through that more than once.
01:56:16<mightybyte>It's just taken awhile for me to get an understanding that I also know how to apply.
01:57:20<mightybyte>Ok, so one more question.
01:58:01<Saizan>yeah, it takes some time.. i found that it helps to have a clear understanding of the type system
01:58:32<mightybyte>In addUser I've got "...unless (not exists) $ modUsers...". How can the caller find out whether the user existed or not?
01:59:00<mightybyte>I guess I need to make it return a "m Maybe ..." instead of "m ()"?
01:59:16<Saizan>unless b m = if b then m else return () <-- it's nothing fancy
01:59:35<Saizan>oh wait
01:59:40<mightybyte>Right, but it returns () whether it adds the user or not.
01:59:51<mightybyte>...because modUsers is also "m ()"
01:59:57<Saizan>yeah
02:00:26<mightybyte>So I need to change addUser to "m (Maybe Bool)" or something similar?
02:00:29<dbpatterson>mightybyte: did you look through HAppS/Agents/Users.hs? or is that from pre 0.9.x code?
02:00:43<Saizan>tbh i've inverted the two branches, unless b m = if b then return () else m
02:01:00<dbpatterson>becuase possibly once it is really working, that would be where your generic framework code should go?
02:01:15<mightybyte>Saizan: Yeah, I know that. But the return type provides know way for the caller to know what happened.
02:01:46<Saizan>mightybyte: i'd just add return exists at the end
02:01:59<mightybyte>dbpatterson: I may have seen it in the process. But yes, I was kind of thinking it would be nice if this code could eventually go back into the project as a generic auth mechanism.
02:03:03<mightybyte>Saizan: Ok. Bound after the unless statement?
02:04:16<Saizan>mightybyte: yes, as a new line of the do-block, that'll change addUser type to .. -> m Bool
02:04:59<mightybyte>So how does the "return ()" fit into the "m Bool"? Does () coerce to anything?
02:06:06<mightybyte>Or does that "return ()" just get bound to the next statement, which ends up not using it?
02:06:32<mightybyte>Never mind, I've got it now.
02:06:38<Saizan>:)
02:07:08<Saizan>you got it faster than i found how to explain it :)
02:07:52<Saizan>btw, i'm going, it's late here
02:08:18<mightybyte>I realized that the return wasn't returning from the whole function, it was just returning a value that got passed to expr2 >>= \_ ->
02:08:40<mightybyte>...still getting trapped by some of my imperative programming tendencies.
02:09:06<mightybyte>Hey, thanks a _lot_ for all the help.
02:09:19<mightybyte>I have a much better picture of things now.
02:09:41<Saizan>i'm glad :)
02:09:50<mightybyte>I really appreciate it.
03:20:31<dbpatterson>I think it was discussed here before, but - in a program, can you have multiple components, and more specifically, is it a good idea?
03:21:14<dbpatterson>for example, one component contains everything with regards to user authentication... should it also contain the other aspects of state?
03:34:33<mightybyte>Yes, I think it was discussed a couple days ago.
03:35:24<mightybyte>"<Lemmih> Components give you transactions and persistence. If you want those assurances you can encaptulate stuff with a component."
03:37:36<mightybyte>And I think it is definitely a good idea to separate components, so you aren't blocking access to all of state while performing an operation that only modifies a small, well-encapsulated part.
03:38:31<mightybyte>But I haven't figured out how to best split things up yet.
03:38:53<dbpatterson>I'm going to put together a simple blog and I'll let you know how it works...
03:39:04<dbpatterson>you understand this stuff better than I do though...
03:43:56<mightybyte>Well...not necessarily. But I'm getting there.
03:46:36<Lemmih>Saizan: Still around?
03:51:28<mightybyte>I think he left awhile ago.
11:39:13<Saizan>mightybyte: using getRandom as the source of SessionKeys i'm worried you can overwrite a still active session
11:51:35<mightybyte>Saizan: I agree with you. In "Intro to HAppS-State", I mention the issue. Although I don't mention it in the next post where I actually present the newSession function.
12:13:37<Saizan>uhm, what is usually done to prevent this?
12:33:42<mightybyte>I don't know. I'm not much of a web programmer. I would assume a CSPRNG should be used to generate much larger numbers at the very least.
14:10:13<Lemmih>Saizan: (re-much earlier) The return type doesn't mension
14:10:17<Lemmih>Tsk.
14:10:31<Lemmih>Saizan: (re-much earlier) The return type doesn't mention the param type.
14:13:30<Saizan>Lemmih: by return type i meant the whole (Query (Session val) Int) which mentions val
14:14:51<Lemmih>query $ NumSessions <- no mention of 'val'.
14:15:34<Lemmih>query $ (NumSessions :: NumSessions val) <- this would do but using a proxy is usually easier.
14:16:50<Saizan>aah ok, so it's just for convenience, not really a requirement
14:17:20<Saizan>to pass the type via a value rather than an annotation
14:17:28<Lemmih>Right.
16:54:48<mightybyte>New post: http://softwaresimply.blogspot.com/2008/02/transactional-integrity-problem.html
18:06:42<Lemmih>HAppS.org is online gain.
18:06:43<Lemmih>*again
18:06:46<mightybyte>Yay!
18:07:08<mightybyte>ACTION goes of to "darcs pull"
22:51:38<porrifolius>Hi folks.
22:52:41<Saizan>hi
22:52:53<porrifolius>Operations on state need to be of type (MonadState State m) => m a or (MonadReader State m) => m a
22:53:18<porrifolius>I'd like to have my operations in (MonadState State m, MonadReader User m) => m a
22:53:36<porrifolius>and have a wrapper like updateAsUser
22:53:49<Saizan>mmh
22:54:18<Saizan>but you don't have a component with type User, right?
22:54:50<porrifolius>nah, User is just a data type in the State, lets say it's just a String
22:55:05<porrifolius>my questions really about monads in general
22:55:52<porrifolius>can you just 'extend' the monad you're using by simply saying updateAsUser user m = runReaderT m user ?
22:56:41<Saizan>yes that will work
22:57:34<Saizan>in that way the operation you pass to mkMethods will still be (MonadState State m) => m a
22:58:09<porrifolius>will I then need to lift get, put etc in the m passed to updateAsUser?
22:58:14<dbpatterson>is there any reason why my type signatures would need MonadIO m as well as MonadReader State m?
22:58:40<dbpatterson>Could not deduce (MonadIO m) from the context (MonadReader State m)
22:58:45<Saizan>dbpatterson: if you're using liftIO or things defined with it.
22:59:23<Saizan>porrifolius: given some instances in the mtl package those operation will "autolift"
22:59:26<Lemmih>dbpatterson: You can't use IO in events, though.
22:59:33<dbpatterson>hmm
22:59:48<dbpatterson>let me strip down an example and I'll post it...
23:00:29<porrifolius>Saizan: but I think I might have trouble with a queryAsUser, because then I'll have two ReaderT's stacked. Is that right?
23:00:50<porrifolius>well, not trouble... but I'd need to lift the ask that gets the State
23:02:04<Saizan>porrifolius: yeah, that's true
23:05:35<Saizan>porrifolius: if your User is taken from State you can just write a function using get/gets and use that, instead of adding another layer
23:08:38<porrifolius>Saizan: could do. It's the User who's logged in so it seems appropriate to be 'fixed' in a Reader monad. And I could want other things not in the State, eg client IP connected or something.
23:11:20<porrifolius>Saizan: perhaps I should be declaring my own QueryAsUser and UpdateAsUser classes, then running those monads in queryAsUser and updateAsUser.
23:11:51<Saizan>porrifolius: well remeber that you must pass those as parameters to the operation, so installing a runReaderT etc.. might be heavyweigth if you don't actually need to pass those things deeply enogh
23:13:02<porrifolius>Saizan: true. The authenticated user seems like it would be pretty pervasive, other things may not be I guess.
23:13:52<porrifolius>would there really be much performance overhead to creating an running another monad though? I thought it would be pretty minor.
23:14:51<Saizan>no, performance is not the problem here
23:15:37<porrifolius>Saizan: so by heavyweight you meant...?
23:15:53<Saizan>i meant in the code
23:17:14<Saizan>but with an ad-hoc primitive like getUser and a suitable instance that removes the need to lift the other get/put/ask it might be nice
23:19:22<Saizan>http://cale.yi.org/index.php/How_To_Use_Monad_Transformers <-- this is a nice tutorial on how to design monads with transformers
23:19:28<porrifolius>Saizan: I think it would be a good idea to refine the primitives available to the operations as well. Eg Users can't be deleted so no deleteUser primitive in UpdateAsUser monad class, but with just MonadState State users could be deleted.
23:19:43<porrifolius>Saizan: Ta. I'll have a look at the tute.
23:23:13<Saizan>porrifolius: sure, that's one of the reasons for having a strong type system. i guess i'm a little biased since all the operations i've seen are very simple
23:27:14<dbpatterson>for startSystemState, how do I use multiple components?
23:27:37<dbpatterson>right now I have entryPoint :: Proxy State
23:27:41<dbpatterson>entryPoint = Proxy
23:27:46<dbpatterson>and then startSystemState entryPoint
23:27:54<dbpatterson>but I now have two components
23:28:03<dbpatterson>State and BlogPosts
23:29:01<Saizan>you can list BlogPosts in the Dependencies of State
23:29:10<Saizan>when you declare State a Component
23:29:43<dbpatterson>so then State will be the top level component, and it will pull in the others?
23:33:01<Saizan>yeah
23:36:38<Saizan>i'm not 100% sure if that's the proper way, but it should work
23:41:57<dbpatterson>Saizan: yep, it's working

Back to channel and daily index: content-negotiated html turtle