@@ -10,185 +10,16 @@ It lives on PyPI at https://pypi.python.org/pypi/effect and GitHub at
1010https://github.com/python-effect/effect.
1111
1212
13- API
14- ===
15-
16- Effect comes with thorough API documentation.
13+ Documentation
14+ =============
1715
1816.. toctree ::
1917 :maxdepth: 3
18+ :numbered:
2019
21- api/effect
22-
23-
24-
25- Explanation by Example
26- ======================
27-
28- Effect starts with a very simple idea: instead of having a function which
29- performs side-effects (such as IO):
30-
31- .. code :: python
32-
33- def get_user_name ():
34- return raw_input (" Enter User Name> " ) # or 'input' in Python 3
35-
36- you instead have a function which *returns * a representation of the
37- side-effect:
38-
39- .. code :: python
40-
41- def get_user_name ():
42- return Effect(ReadLine(" Enter User Name> " ))
43-
44- We call objects like ``ReadLine `` an *intent * -- that is, the *intent * of this
45- effect is to read a line of input from the user. Ideally, intents are very
46- simple objects with public attributes and no behavior, only data.
47-
48- .. code :: python
49-
50- class ReadLine (object ):
51- def __init__ (self , prompt ):
52- self .prompt = prompt
53-
54- To perform the ReadLine intent, we must implement a performer function:
55-
56- .. code :: python
57-
58- @sync_performer
59- def perform_read_line (dispatcher , readline ):
60- return raw_input (readline.prompt)
61-
62-
63- To do something with the result of the effect, we must attach callbacks with
64- the ``on `` method:
65-
66- .. code :: python
67-
68- def greet ():
69- return get_user_name().on(
70- success = lambda r : Effect(Print(" Hello," , r)),
71- error = lambda exc_info : Effect(Print(" There was an error!" , exc_info[1 ])))
72-
73-
74- (Here we assume another intent, ``Print ``, which shows some text to the user.)
75-
76- A (sometimes) nicer syntax is provided for adding callbacks, with the
77- :func: `effect.do.do ` decorator.
78-
79- .. code :: python
80-
81- from effect.do import do
82-
83- @do
84- def greet ():
85- try :
86- name = yield get_user_name()
87- except Exception as e:
88- yield Effect(Print(" There was an error!" , e))
89- else :
90- yield Effect(Print(" Hello," , name))
91-
92- Finally, to actually perform these effects, they can be passed to
93- :func: `effect.perform `, along with a dispatcher which looks up the performer
94- based on the intent.
95-
96- .. code :: python
97-
98- def main ():
99- eff = greet()
100- dispatcher = TypeDispatcher({ReadLine: perform_read_line})
101- perform(dispatcher, effect)
102-
103- This has a number of advantages. First, your unit tests for ``get_user_name ``
104- become simpler. You don't need to mock out or parameterize the ``raw_input ``
105- function - you just call ``get_user_name `` and assert that it returns a
106- ``ReadLine `` object with the correct 'prompt' value.
107-
108- Second, you can implement ``ReadLine `` in a number of different ways - it's
109- possible to override the way an intent is performed to do whatever you want. For
110- example, you could implement an HTTPRequest client either using the popular
111- `requests `_ package, or using the Twisted-based `treq `_ package -- without
112- needing to change any of your application code, since it's all in terms of the
113- Effect API.
114-
115- .. _`requests` : https://pypi.python.org/pypi/requests
116- .. _`treq` : https://pypi.python.org/pypi/treq
117-
118-
119- A quick tour, with definitions
120- ==============================
121-
122- - Intent: An object which describes a desired action, ideally with simple
123- inert data in public attributes. For example, ``ReadLine(prompt='> ') `` could
124- be an intent that describes the desire to read a line from the user after
125- showing a prompt.
126- - :obj: `effect.Effect `: An object which binds callbacks to receive the result
127- of performing an intent.
128- - Performer: A callable that takes the Dispatcher, an Intent, and a Box. It
129- executes the Intent and puts the result in the Box. For example, the
130- performer for ``ReadLine() `` could call ``raw_input(intent.prompt) ``.
131- - Dispatcher: A callable that takes an Intent and finds the Performer that can
132- execute it (or None). See :obj: `TypeDispatcher ` and :obj: `ComposedDispatcher `
133- for handy pre-built dispatchers.
134- - Box: An object that has ``succeed `` and ``fail `` methods for providing the
135- result of an effect (potentially asynchronously). Usually you don't need
136- to care about this, if you define your performers with
137- :func: `effect.sync_performer ` or :func: `effect.twisted.deferred_performer `.
138-
139- There's a few main things you need to do to use Effect.
140-
141- - Define some intents to describe your side-effects (or use a library
142- containing intents that already exist). For example, an ``HTTPRequest ``
143- intent that has ``method ``, ``url ``, etc attributes.
144- - Write your application code to create effects like
145- ``Effect(HTTPRequest(...)) `` and attach callbacks to them with
146- :func: `Effect.on `.
147- - As close as possible to the top-level of your application, perform your
148- effect(s) with :func: `effect.perform `.
149- - You will need to pass a dispatcher to :func: `effect.perform `. You should create one
150- by creating a :class: `effect.TypeDispatcher ` with your own performers (e.g. for
151- ``HTTPRequest ``), and composing it with :obj: `effect.base_dispatcher ` (which
152- has performers for built-in effects) using :class: `effect.ComposedDispatcher `.
153-
154-
155- Callback chains
156- ===============
157-
158- Effect allows you to build up chains of callbacks that process data in turn.
159- That is, if you attach a callback ``a `` and then a callback ``b `` to an Effect,
160- ``a `` will be called with the original result, and ``b `` will be called with
161- the result of ``a ``. This is exactly how Twisted's Deferreds work, and similar
162- to the monadic ``bind `` (``>>= ``) function from Haskell.
163-
164- This is a great way to build abstractions, compared to non-chaining callback
165- systems like Python's Futures. You can easily build abstractions like the
166- following:
167-
168- .. code :: python
169-
170- def request_url (method , url , str_body ):
171- """ Perform an HTTP request."""
172- return Effect(HTTPRequest(method, url, str_body))
173-
174- def request_200_url (method , url , str_body ):
175- """
176- Perform an HTTP request, and raise an error if the response is not 200.
177- """
178- def check_status (response ):
179- if response.code != 200 :
180- raise HTTPError(response.code)
181- return response
182- return request_url(method, url, str_body).on(success = check_status)
183-
184- def json_request (method , url , dict_body ):
185- """
186- Perform an HTTP request where the body is sent as JSON and the response
187- is automatically decoded as JSON if the Content-type is
188- application/json.
189- """
190- str_body = json.dumps(dict_body)
191- return request_200_url(method, url, str_body).on(success = decode_json)
20+ intro
21+ testing
22+ apidocs
19223
19324
19425Indices and tables
0 commit comments