Re: Encapsulating Local Mutable State

2016-05-02 Thread Stefan Kamphausen
Good point, Paul.  Thanks.



Best,
stefan

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: Encapsulating Local Mutable State

2016-04-27 Thread Paul Mooser
One thing you can do is use 'reify', which will allow you to implement a 
protocol but also have your state atom hidden in a closure. You'll still be 
passing an 'object' to all of the protocol methods (obviously), but it 
won't be the atom itself. 

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: Encapsulating Local Mutable State

2016-04-16 Thread Stefan Kamphausen
Hi Francis,


Thanks for taking the time to thoroughly explain your approach.  I find it 
interesting and was not yet aware of it.  Need to wrap my head around it a 
bit.


Best,
Stefan

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: Encapsulating Local Mutable State

2016-04-15 Thread Francis Avila
If you can model the authentication process as a state machine, have a pure 
function which accepts auth-state and data and returns either a new state 
or an operation to get new data to determine the next state.

E.g. (next-auth-state {:stage :not-authed :login "login" :password "pass"} 
nil) => {:state {:stage :not-authed ...} :operations [{:kind 
:get-auth-token :args {:login "login" ...}]}}.  Then (next-auth-state 
{:stage :not-authed} {:auth-token ""}) => {state {:stage :has-token :token 
"..." :expires "..."} :operations [{:kind :get-session-token :args 
{:session-token "token"}{:kind :invalidate-token :args {:session-token 
"token"}}] etc.

In state machine graph terms, the :stage is the node you are on, the 
:operations are actions which you can take to return data which represent 
the edges the state machine can follow.

Then you have a higher-level reduction function which accepts any auth 
state map and keeps running the state machine until it can return a map 
that is suitable for authentication. This function is responsible (possibly 
indirectly) for turning operation requests into http calls and collecting 
the responses, i.e. is impure, but not necessarily stateful or mutable.

E.g. auth-state map could start in {:stage :not-authed :login "login" 
:password "pass"}. (authorize auth-state) is a reduction function that runs 
(get-auth-token auth-state), returns {:stage :token :expires #inst"..."}, 
then authorize inspects return value and runs (open-session {:stage :token 
:expires "..."}) etc, until either a failure or success.

Lower-level client api calls receive only auth-maps with a non-expired 
stage=:session. They must return, in addition to success or failure of the 
call itself, any auth-related state info from the server which should alter 
the auth-map somehow. Another function (maybe authorize, maybe something 
else) integrates this state with the passed-in auth map and returns a new 
(possibly unchanged) auth-map, which can be used for future calls.


Higher-level client api functions should combine api calls with auth-state 
updating. If you keep your fn argument and return structure regular enough, 
you might even be able to do this with a single higher-order function: 
(call-with-auth low-level-client-api-fn auth-map args) -> {:result x 
:auth-map new-auth-map-state}

Only at this point might it be convenient to have a higher-level, stateful 
construct which completely hides the auth-state map from you. e.g. 
(make-session login pass) => {:auth-state (atom {:state :not-authed :login 
login :pass pass})},
then (call-with-session session client-fn args) => result-only, and mutates 
the auth state in the atom for you. But the emphasis here is on programmer 
convenience (not having to collect the new-auth-state return value and 
propagate it around), not any necessity of modeling the auth flow.

You still need to think about concurrency at the communication level. If 
two client api calls run at the same time, both with expired tokens, what 
can happen? Can both independently run the update with separate http calls 
and get different tokens (or will the server issue the same token to both)? 
Should only one http communication happen on the client side and the client 
is responsible for blocking callers that want a new token until a single 
token is reissued?
Your decision affects the implementation of the state-machine driving 
function (the one that executes operations and calls next-state 
repeatedly): you might put a token and session cache in there, or put 
operations related to the same login on their own queue so there is only a 
single writer per login, or whatever. But the hard auth flow logic in your 
next-state function remains the same.


On, Friday, April 15, 2016 at 7:40:48 AM UTC-5, Stefan Kamphausen wrote:
>
> Hi,
>
>
> Currently, I am in the process of writing a client to server API which is 
> not trivial to consume.  In particular it needs a 3-step authentication 
> process: login with user name and password, get an authentication token, 
> open a session with the token and finally consume the API with the 
> session.  Sessions and tokens can expire and the client should handle that 
> transparently: if it has a token, create a new session, if the token 
> expired and it has username and password, create a new token...  So, 
> sessions and tokens would have to be local, mutable, encapsulated state, as 
> far as I can see.
>
> Now; I wonder how to best model this in Clojure.
>
> My favorite right now is, creating a closure over a local atom and return 
> it to the user.  The downside to this is that it feels unnatural to consume 
> different parts of the API, e.g. (client :do-something ) vs (client 
> :do-something-else & other-args).  It would be nice to defined a protocol 
> with function do-something and do-something-else but then I would have to 
> pass the atom as an argument to the record which feels even worse.
>
> Am I missing an obvious other solution? 

Encapsulating Local Mutable State

2016-04-15 Thread Stefan Kamphausen
Hi,


Currently, I am in the process of writing a client to server API which is 
not trivial to consume.  In particular it needs a 3-step authentication 
process: login with user name and password, get an authentication token, 
open a session with the token and finally consume the API with the 
session.  Sessions and tokens can expire and the client should handle that 
transparently: if it has a token, create a new session, if the token 
expired and it has username and password, create a new token...  So, 
sessions and tokens would have to be local, mutable, encapsulated state, as 
far as I can see.

Now; I wonder how to best model this in Clojure.

My favorite right now is, creating a closure over a local atom and return 
it to the user.  The downside to this is that it feels unnatural to consume 
different parts of the API, e.g. (client :do-something ) vs (client 
:do-something-else & other-args).  It would be nice to defined a protocol 
with function do-something and do-something-else but then I would have to 
pass the atom as an argument to the record which feels even worse.

Am I missing an obvious other solution? Have you done something similar, 
how?

Any pointers welcome.


Best regards,
stefan

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.