Skip to content

Commit b716713

Browse files
committed
Implementing PromiseEC based on ErrorT and ContT
Based on discussions in issue #18 and pull request #20
1 parent a0766e3 commit b716713

File tree

8 files changed

+177
-32
lines changed

8 files changed

+177
-32
lines changed

MODULE.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@
381381

382382
data Http :: *
383383

384-
type HttpResponse e r a b c d = HttpEff e (Promise (Response r a b c d) (Response r a b c d))
384+
type HttpResponse e r a b c d = HttpEff e (Promise I.ForeignResponse (Response r a b c d))
385385

386386
type Response r a b c d = { statusText :: String, config :: Config a b c d, headers :: [String] -> String, status :: Status, "data" :: D.HttpData r }
387387

@@ -681,13 +681,19 @@
681681

682682
data Promise :: * -> * -> *
683683

684+
type PromiseEC e a b = ErrorT a (ContT Unit (Eff e)) b
685+
684686

685687
### Values
686688

687689
catch :: forall a b c d. (a -> Promise c d) -> Promise a b -> Promise c d
688690

689691
finally :: forall e r a b. Eff e r -> Promise a b -> Promise a b
690692

693+
liftPromiseEC :: forall e a b. (Error a) => Eff e (Promise a b) -> PromiseEC e a b
694+
695+
runPromiseEC :: forall e a b. PromiseEC e a b -> (Either a b -> Eff e Unit) -> Eff e Unit
696+
691697
then1 :: forall a b c. (b -> Promise a c) -> Promise a b -> Promise a c
692698

693699
then1' :: forall a b c. (b -> c) -> Promise a b -> Promise a c
@@ -884,6 +890,11 @@
884890
data ForeignResponse :: *
885891

886892

893+
### Type Class Instances
894+
895+
instance errorForeignResponse :: Error ForeignResponse
896+
897+
887898
### Values
888899

889900
foreignConfig :: forall e. HttpEff e ForeignConfig

bower.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"purescript-exceptions": "0.2.1",
2222
"purescript-foldable-traversable": "0.1.4",
2323
"purescript-maybe": "0.2.1",
24+
"purescript-transformers": "0.3.0",
2425
"purescript-tuples": "0.2.1",
2526
"purescript-simple-dom": "git://github.com/aktowns/purescript-simple-dom.git"
2627
},

examples/Backend/Main.purs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module Backend.Main where
2+
3+
import Control.Monad.Eff
4+
import Data.Either (either)
5+
6+
import Angular.Http (Http(), get)
7+
import Angular.Module (controller, ngmodule')
8+
import Angular.Promise (liftPromiseEC, runPromiseEC)
9+
import Angular.This (extendThis)
10+
11+
mainctrl http this = extendThis { text: ""
12+
, submit: submit } this
13+
where
14+
url = "http://localhost:9501/examples/Backend/main.html"
15+
submit a = runPromiseEC (do p <- liftPromiseEC $ get url http
16+
q <- liftPromiseEC $ get url http
17+
return q) (either (\e -> extendThis { value: "Failed to get" ++ url } this)
18+
(\a -> extendThis { value: a."data" } this))
19+
20+
main = do
21+
m <- ngmodule' "backend" []
22+
controller "Main" mainctrl' m
23+
24+
foreign import mainctrl'
25+
"""
26+
/*@ngInject*/function mainctrl$prime($http) {
27+
var impl = mainctrl($http)(this);
28+
return impl.apply(this, []);
29+
}
30+
""" :: forall e a. Http -> Eff e Unit

examples/Backend/main.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!doctype html>
2+
<html class="no-js"
3+
lang=""
4+
ng-app="backend">
5+
<head>
6+
<meta charset="utf-8">
7+
<meta http-equiv="X-UA-Compatible"
8+
content="IE=edge">
9+
<title>Backend</title>
10+
<meta name="description"
11+
content="">
12+
<meta name="viewport"
13+
content="width=device-width, initial-scale=1">
14+
<link rel="stylesheet"
15+
href="../../bower_components/angular/angular-csp.css">
16+
</head>
17+
<body ng-controller="Main as mainctrl">
18+
<form ng-submit="mainctrl.submit(mainctrl.text)()"
19+
novalidate>
20+
<button type="submit">
21+
Submit
22+
</button>
23+
</form>
24+
<code ng-cloak>
25+
{{mainctrl.value.value0}}
26+
</code>
27+
<script src="../../bower_components/angular/angular.js"></script>
28+
<script src="../../dist/backend.js"></script>
29+
</body>
30+
</html>

gulpfile.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@ var gulp = require('gulp')
1313
'src/**/*.purs'
1414
],
1515
examples: {
16-
todomvc: 'examples/Todomvc/**/*.purs'
16+
todomvc: {
17+
src: 'examples/Todomvc/**/*.purs',
18+
options: {
19+
main: 'Todomvc.Main',
20+
output: 'todomvc.js'
21+
}
22+
},
23+
backend: {
24+
src: 'examples/Backend/**/*.purs',
25+
options: {
26+
main: 'Backend.Main',
27+
output: 'backend.js'
28+
}
29+
}
1730
},
1831
dest: 'dist',
19-
docs: 'MODULE.md',
20-
options: {
21-
main: 'Todomvc.Main',
22-
output: 'todomvc.js'
23-
}
32+
docs: 'MODULE.md'
2433
},
2534
nstatic: {
2635
root: '.',
@@ -55,9 +64,19 @@ gulp.task('clean', function(){
5564

5665
gulp.task('todomvc', ['clean'], function(){
5766
return (
58-
gulp.src([config.purescript.examples.todomvc].concat(config.purescript.src)).
67+
gulp.src([config.purescript.examples.todomvc.src].concat(config.purescript.src)).
5968
pipe(plumber()).
60-
pipe(purescript.psc(config.purescript.options)).
69+
pipe(purescript.psc(config.purescript.examples.todomvc.options)).
70+
on('error', error).
71+
pipe(gulp.dest(config.purescript.dest))
72+
);
73+
});
74+
75+
gulp.task('backend', ['clean'], function(){
76+
return (
77+
gulp.src([config.purescript.examples.backend.src].concat(config.purescript.src)).
78+
pipe(plumber()).
79+
pipe(purescript.psc(config.purescript.examples.backend.options)).
6180
on('error', error).
6281
pipe(gulp.dest(config.purescript.dest))
6382
);
@@ -96,7 +115,12 @@ gulp.task('watch', function(cb){
96115
});
97116

98117
gulp.task('watch.todomvc', function(cb){
99-
gulp.watch([config.purescript.examples.todomvc].concat(config.purescript.src), ['todomvc']);
118+
gulp.watch([config.purescript.examples.todomvc.src].concat(config.purescript.src), ['todomvc']);
119+
server(cb);
120+
});
121+
122+
gulp.task('watch.backend', function(cb){
123+
gulp.watch([config.purescript.examples.backend.src].concat(config.purescript.src), ['backend']);
100124
server(cb);
101125
});
102126

src/Angular/Http.purs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ import qualified Data.DOM.Simple.Ajax as D
2929
import Angular.Cache (Cache())
3030
import qualified Angular.Http.Internal as I
3131
import Angular.Http.Types
32-
import Angular.Promise (Promise(), then2')
32+
import Angular.Promise (Promise(), then1')
3333

3434
foreign import data Http :: *
3535

36-
type HttpResponse e r a b c d = HttpEff e (Promise (Response r a b c d) (Response r a b c d))
36+
type HttpResponse e r a b c d = HttpEff e (Promise I.ForeignResponse (Response r a b c d))
3737

3838
type ForeignHttpResponse e = HttpEff e (Promise I.ForeignResponse I.ForeignResponse)
3939

@@ -77,7 +77,7 @@ config = { method: D.GET
7777
, responseType: D.Default }
7878

7979
http :: forall e r a b c d. Config a b c d -> Http -> HttpResponse e r a b c d
80-
http c h = (then2' foreignResponse foreignResponse) <$> (foreignConfig c >>= runFn2 httpFn h)
80+
http c h = (then1' foreignResponse) <$> (foreignConfig c >>= runFn2 httpFn h)
8181

8282
get :: forall e r a b c d. D.Url -> Http -> HttpResponse e r a b c d
8383
get u = runHttpFn' D.GET u config
@@ -119,13 +119,13 @@ runHttpFn' :: forall e r a b c d. D.HttpMethod -> D.Url -> Config a b c d -> Htt
119119
runHttpFn' m u c h = do
120120
conf <- foreignConfig c
121121
res <- runFn4 httpFn' (show m) u conf h
122-
return $ then2' foreignResponse foreignResponse res
122+
return $ then1' foreignResponse res
123123

124124
runHttpFn'' :: forall e r a b c d. D.HttpMethod -> D.Url -> RequestData b -> Config a b c d -> Http -> HttpResponse e r a b c d
125125
runHttpFn'' m u d c h = do
126126
conf <- foreignConfig c
127127
res <- runFn5 httpFn'' (show m) u (writeRequestData d) conf h
128-
return $ then2' foreignResponse foreignResponse res
128+
return $ then1' foreignResponse res
129129

130130
foreignConfig :: forall e a b c d. Config a b c d -> HttpEff e I.ForeignConfig
131131
foreignConfig conf = do

src/Angular/Http/Internal.purs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module Angular.Http.Internal
3232
) where
3333

3434
import Control.Monad.Eff
35+
import Control.Monad.Error
3536
import Data.Either
3637
import Data.Foldable (for_)
3738
import Data.Function
@@ -47,6 +48,18 @@ foreign import data ForeignConfig :: *
4748

4849
foreign import data ForeignResponse :: *
4950

51+
foreign import unimplementedForeignResponse
52+
"""
53+
function foreignResponse(){
54+
return {
55+
};
56+
}
57+
""" :: ForeignResponse
58+
59+
instance errorForeignResponse :: Error ForeignResponse where
60+
noMsg = unimplementedForeignResponse
61+
strMsg = \_ -> unimplementedForeignResponse
62+
5063
setConfigMethod :: forall e. D.HttpMethod -> ForeignConfig -> HttpEff e Unit
5164
setConfigMethod m = runFn3 setConfigPropFn "method" (show m)
5265

src/Angular/Promise.purs

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Angular.Promise
22
( Promise()
3+
, PromiseEC()
34
, then1
45
, then1'
56
, then2
@@ -8,18 +9,28 @@ module Angular.Promise
89
, then3'
910
, catch
1011
, finally
12+
, liftPromiseEC
13+
, runPromiseEC
1114
) where
1215

16+
import Control.Monad.Cont.Trans (ContT(..), runContT)
1317
import Control.Monad.Eff
18+
import Control.Monad.Error (Error)
19+
import Control.Monad.Error.Trans (ErrorT(..), runErrorT)
20+
import Control.Monad.Trans (lift)
21+
22+
import Data.Either (Either(Right, Left))
1423
import Data.Function
1524

1625
foreign import data Promise :: * -> * -> *
1726

27+
type PromiseEC e a b = ErrorT a (ContT Unit (Eff e)) b
28+
1829
foreign import then1Fn
1930
"""
20-
function then1Fn(f, fa){
21-
return fa['then'](f);
22-
}
31+
function then1Fn(f, fa){
32+
return fa['then'](f);
33+
}
2334
""" :: forall a b c d. Fn2 (b -> c) (Promise a b) (Promise a d)
2435

2536
then1 :: forall a b c. (b -> Promise a c) -> Promise a b -> Promise a c
@@ -30,9 +41,9 @@ then1' = runFn2 then1Fn
3041

3142
foreign import then2Fn
3243
"""
33-
function then2Fn(f, g, fa){
34-
return fa['then'](f, g);
35-
}
44+
function then2Fn(f, g, fa){
45+
return fa['then'](f, g);
46+
}
3647
""" :: forall s t a b c d. Fn3 (b -> d) (a -> c) (Promise a b) (Promise s t)
3748

3849
then2 :: forall a b c d. (b -> Promise c d) -> (a -> Promise c d) -> Promise a b -> Promise c d
@@ -43,11 +54,11 @@ then2' = runFn3 then2Fn
4354

4455
foreign import then3Fn
4556
"""
46-
function then3Fn(f, g, h, fa){
47-
return fa['then'](f, g, function(a){
48-
return h(a)();
49-
});
50-
}
57+
function then3Fn(f, g, h, fa){
58+
return fa['then'](f, g, function(a){
59+
return h(a)();
60+
});
61+
}
5162
""" :: forall e q r s t a b c d. Fn4 (b -> d)
5263
(a -> c)
5364
(s -> Eff e t)
@@ -62,20 +73,45 @@ then3' = runFn4 then3Fn
6273

6374
foreign import catchFn
6475
"""
65-
function catchFn(f, fa){ \
66-
return fa['catch'](f); \
67-
}
76+
function catchFn(f, fa){
77+
return fa['catch'](f);
78+
}
6879
""" :: forall a b c d. Fn2 (a -> Promise c d) (Promise a b) (Promise c d)
6980

7081
catch :: forall a b c d. (a -> Promise c d) -> Promise a b -> Promise c d
7182
catch = runFn2 catchFn
7283

7384
foreign import finallyFn
7485
"""
75-
function finallyFn(f, fa){
76-
return fa['finally'](f);
77-
}
86+
function finallyFn(f, fa){
87+
return fa['finally'](f);
88+
}
7889
""" :: forall e r a b. Fn2 (Eff e r) (Promise a b) (Promise a b)
7990

8091
finally :: forall e r a b. Eff e r -> Promise a b -> Promise a b
8192
finally = runFn2 finallyFn
93+
94+
foreign import then2ECFn
95+
"""
96+
function then2ECFn(f, g, fa){
97+
return function(){
98+
var run = function(k){
99+
return function(a){
100+
return k(a)();
101+
};
102+
};
103+
fa['then'](run(f), run(g));
104+
};
105+
}
106+
""" :: forall e a b c d. Fn3 (b -> d) (a -> c) (Promise a b) (Eff e Unit)
107+
108+
promiseEC :: forall e a b. Promise a b -> PromiseEC e a b
109+
promiseEC = ErrorT <<< ContT <<< cb
110+
where
111+
cb fa k = runFn3 then2ECFn (k <<< Right) (k <<< Left) fa
112+
113+
liftPromiseEC :: forall e a b. (Error a) => Eff e (Promise a b) -> PromiseEC e a b
114+
liftPromiseEC fa = (lift $ lift fa) >>= promiseEC
115+
116+
runPromiseEC :: forall e a b. PromiseEC e a b -> (Either a b -> Eff e Unit) -> Eff e Unit
117+
runPromiseEC = runContT <<< runErrorT

0 commit comments

Comments
 (0)