how to use Play 2.1.1 async requests and futures - asynchronous

The code below doesn't compile and I don't know how to fix it.
def doAsync(n: Int) = Action {
import scala.concurrent.ExecutionContext.Implicits.global
Async {
val f1 = Future.successful(n)
f1.map(x => x match {
case 10 => Ok("first")
case _ => {
val f2 = Future.successful(n)
f2.map(y => Ok("second"))
}
})
}
}
The line f2.map(y => Ok("second")) produces the compile error:
type mismatch; found:
scala.concurrent.Future[play.api.mvc.SimpleResult[String]] required:
play.api.mvc.Result
The core "business logic" is: f2 only must run if n is not 10.

f1 is going to be a future of a result.
You're returning a result for case 10, and a a future of a result otherwise. You need to put the result into the same terms. Try:
f1 flatMap {x => x match {
case 10 => Future.successful(Ok("first"))
case _ => {
val f2 = Future.successful(n)
f2.map(y => Ok("second"))
}
}

Related

Flow type a generic function that groups any type that has at least an ID

I'm trying to type with Flow a function that maps As to Bs, where the only restrictions are:
B contains an A
A has at least an id property which is a string
Apart from that, A can be any object, and, in this situation, B is well known.
I want to type the function with a generic/polymorphic type that so the type checker knows that you will get an array of objects containing the A and B that matches.
My attempt below does not give me any type error, but I don't think it is correct either.
Will love to understand how to properly type this so you can get the most guarantees.
type B = {A: {id: string}}
const BContainsA = (id: string) => (b: B) =>
b.A.id === id
type MapResult<T> = {
AsWithBs: Array<{ A: T, B: B }>,
AsWithoutBs: string[],
}
const mapAsToBs = <T>(
As: { ...T, id: string }[],
Bs: B[]
): MapResult<T> => {
return As.reduce(
(result, a) => {
const b = Bs.find(BContainsA(a.id))
if (!b) {
result.AsWithoutBs.push(a.id)
return result
}
result.AsWithBs.push({ A: a, B: b })
return result
},
{ AsWithBs: [], AsWithoutBs: [] }
)
}
mapAsToBs([{pos:2,id: '1'},{pos:1,id: '11'}],[{A:{id: '1'}}])
Seems that all I had to do was to add a constraint to the generic type like this:
const mapAsToBs = <T:{id: string}>(
As: T[],
Bs: B[]
): MapResult<T> => {
}
It is indeed documented, but the syntax is so unintuitive and the explanation is so short, that I would have never guessed it just by reading it.
You can check how it works as expected here

Is there a better way to compose this in a point-free way?

I come across this use-case at work all the time and I feel like there must be a way to compose fullName in a point-free way without defining cat as a parameter:
const cats = [
{ name: 'Bob', lastName: 'Ross' },
{ name: 'Frank', lastName: 'Langella' },
];
// this bugs me
const fullName = cat => add(
prop('name', cat),
prop('lastName', cat)
);
const isEqual = curry((a, b) => a === b);
const isBobRoss = compose(isEqual('BobRoss'), fullName);
edit: some of the helpers above in case it helps understand the challenge
/**
* compose :: ((a -> b), (b -> c), ..., (y -> z)) -> a -> z
*/
const compose = (...fns) => (...args) =>
fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
/**
* curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
*/
function curry(fn) {
const arity = fn.length;
return function $curry(...args) {
if (args.length < arity) {
return $curry.bind(null, ...args);
}
return fn.call(null, ...args);
};
/**
* add :: a -> b -> a + b
*/
const add = curry((x, y) => x + y)
/**
* prop :: String -> Object -> a
*/
const prop = curry((p, obj) => obj[p])
}
This is no exactly function composition, but sure you can write a helper function for it. Ramda does know it as converge, here's a simplified (unary) version of it:
const converge = (fn, wraps) => arg => fn(...wraps.map(wrap => wrap(arg)));
cost fullName = converge(add, [prop('name'), prop('lastName')]);
(Initially I decline to commit to a specific functional programming library for JS/TS and work in general concepts)
Notice that your curred prop function takes a key and returns a reader monad. This will be useful.
Assuming type Obj = {name:string,lastName:string}, then your curried prop fn is (key:'name'|'lastName') => Reader<Obj,string>
You can use a sequence type function to combine two reader monads into a single one, as such:
const getNameParts = sequence(prop('name'), prop('lastName')) // Reader<Obj, [string,string]>
Then you can map the [string,string] to be a single string like in your add function
const add = as => as.reduce((acc, item) => acc + item)
So if you can lift add into your reader monad's computational context (here using a proposed `map: ((a:A)=>B)=>(fa:F<A>)=>F<B>), then compose these operations:
const buildNameFromObject = compose(getNameParts, map(add)) // Reader<Obj, string>
There we have it.
const personsName = buildNameFromObject(someObject) // string
fp-ts is a library that provides everything I just mentioned, and using that library (with a few function name changes to align with fp-ts's vocabulary),
import { reader, map } from 'fp-ts/lib/Reader'
import { sequenceT } from 'fp-ts/lib/Apply'
import { pipe } from 'fp-ts/lib/pipeable'
const getNameParts = sequenceT(reader)(prop('name'), prop('lastName'))
const buildNameFromObject = pipe(getNameParts, map(add)) // Reader<Obj, string>, which is a fancy way of writing (o:Obj)=>string
buildNameFromObject({name:'Foo', lastName: 'Bar'}) // 'FooBar'
Your "fullName" function (buildNameFromObject) is now point free.

Unexpected text "return"

I'm trying to implement an AuthService from a tutorial of Fireship (https://fireship.io/lessons/flutter-firebase-google-oauth-firestore/)
I copied exactly his AuthService:
AuthService() {
user = Observable(_auth.onAuthStateChanged);
profile = user.switchMap((FirebaseUser u) => {
if (u != null) {
return _db.collection("users").document(u.uid).snapshots().map((snap) => snap.data);
} else {
return Observable.just({});
}
});
}
I get these errors:
If I copy the code from his website (it's exactly the same) there are no errors.
wtf? Can someone explain this or help? Thanks!
Change this:
profile = user.switchMap((FirebaseUser u) => {
into this:
profile = user.switchMap((FirebaseUser u) {
From the docs:
For functions that contain just one expression, you can use a shorthand syntax:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
The => expr syntax is a shorthand for { return expr; }. The => notation is sometimes referred to as arrow syntax.

How to replace combinators with Future?

I have a function which returns Future. It accepts another function which accepts one argument and returns Future. Second function can be implemented as combinators chain passed into first function. It looks like this:
use bb8::{Pool, RunError};
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::{error::Error, Client, NoTls};
#[derive(Clone)]
pub struct DataManager(Pool<PostgresConnectionManager<NoTls>>);
impl DataManager {
pub fn new(pool: Pool<PostgresConnectionManager<NoTls>>) -> Self {
Self(pool)
}
pub fn create_user(
&self,
reg_req: UserRequest,
) -> impl Future<Item = User, Error = RunError<Error>> {
let sql = "long and awesome sql";
let query = move |mut conn: Client| { // function which accepts one argument and returns Future
conn.prepare(sql).then(move |r| match r {
Ok(select) => {
let f = conn
.query(&select, &[&reg_req.email, &reg_req.password])
.collect()
.map(|mut rows| {
let row = rows.remove(0);
row.into()
})
.then(move |r| match r {
Ok(v) => Ok((v, conn)),
Err(e) => Err((e, conn)),
});
Either::A(f)
}
Err(e) => Either::B(future::err((e, conn))),
})
};
self.0.run(query) // function which returns Future and accepts another function
}
}
But I want to write code of create_user as a struct implementing Future.
struct UserCreator(Pool<PostgresConnectionManager<NoTls>>, UserRequest);
impl UserCreator {
fn new(pool: Pool<PostgresConnectionManager<NoTls>>, reg_req: UserRequest) -> Self {
Self(pool, reg_req)
}
}
How to implement Future for this struct that works as first function? Please help me with an example.
Now I tried to make it like this, but nothing is computed and execution always blocks.
impl Future for UserCreator {
type Item = User;
type Error = RunError<Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// Code which which works like `DataManager.create_user`
let sql = "long and awesome sql";
let reg_req = &self.1;
let query = move |mut conn: Client| {
conn.prepare(sql).then(move |r| match r {
Ok(select) => {
let f = conn
.query(&select, &[&reg_req.email, &reg_req.password])
.collect()
.map(|mut rows| {
let row = rows.remove(0);
row.into()
})
.then(move |r| match r {
Ok(v) => Ok((v, conn)),
Err(e) => Err((e, conn)),
});
Either::A(f)
}
Err(e) => Either::B(future::err((e, conn))),
})
};
self.0.run(query).poll()
}
}

FlowType errors using Object.entries

So, I have the following code, but flow errors keep popping up. I've tried to cast the Object.entries, but just won't work - others things to. Any insight?
type Fields = {
name: string,
func: (*) => boolean
};
type S = {
key1: Fields,
bill: Fields
}
var a: S = {
key1: {name: 'mary', func: (str) => str === 'mary'},
bill: {name: 'bill', func: (str) => str === 'bill'}
}
var c = Object
.entries(a)
.map(([key, obj]) => obj.func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});
I've left some things off, but the slow is the same. Flow is reading that entries as a return Tuple Type. I've tried all sorts of things, but instead of mudding things up, I left it untouched.
I can't seem to annotate the destructured items here ([key, obj]), get tuple errors...
Any assistance on getting that code assigned to var c, to work with annotations etc..?
The errors I get:
Cannot call method on mixed type (from obj.func)
Cannot assign value in Tuple etc..
The error is accurate. Object.entries has the type
entries(object: any): Array<[string, mixed]>;
It has no way to know what the type of the second item in the tuple will be. That means your code
.map(([key, obj]) => obj.func(key) ? obj : false)
would need to do
.map(([key, obj]) => {
if (typeof obj.func !== 'function') throw new Error();
return obj.func(key) ? obj : false;
})
so that flow knows that it is guaranteed to be a function.
Alternatively, you could change your data structure to use a type where the second item in the tuple has a guaranteed type, like Map, e.g.
type Fields = {
name: string,
func: (string) => boolean
};
type S = Map<string, Fields>;
var a: S = new Map([
['key1', {name: 'mary', func: (str) => str === 'mary'}],
['bill', {name: 'bill', func: (str) => str === 'bill'}],
]);
var c = Array.from(a, ([key, obj]) => obj.func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});
In my case, I had:
let objectsByName : { [string] : MyObjectType } = {}; //simple map
...
objectsByName[object.name] = object; //call repeatedly to populate map.
...
let results : any[] = []; //next we will populate this
Trying to operate on it like this failed for Flow (though this is executable JavaScript):
for (let [name : string, object : MyObjectType] of Object.entries(objectsByName))
{
let result = doSomethingWith(object); //<- error on arg
results.push(result);
}
This succeeded for Flow:
for (let name : string in objectsByName)
{
let object = objectsByName[name];
let result = doSomethingWith(object); //<- error on arg
results.push(result);
}
It is annoying having to change code structure to suit a supposedly non-intrusive system like Flow comment types, which I chose in the hopes of making my code completely oblivious to Flow's presence. In this case I have to make an exception and structure my code as Flow wants it.
Replacing Object.entries with Object.keys + lookup fixes flow errors for me assuming the input object is properly typed.
i.e. replace Object.entries(a) with Object.keys(a).map(key => [key, a[key]])
This works with flow:
type Fields = {
name: string,
func: (*) => boolean
};
type S = {
key1: Fields,
bill: Fields
}
var a: S = {
key1: {name: 'mary', func: (str) => str === 'mary'},
bill: {name: 'bill', func: (str) => str === 'bill'}
}
var c = Object
.keys(a)
.map(key => a[key].func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});

Resources