I have the next code:
import zio._
import scala.concurrent.Future
case class AppError(description: String) extends Throwable
// legacy-code imitation
def method(x: Int): Task[Boolean] = {
Task.fromFuture { implicit ec => Future.successful(x == 0) }
}
def handler(input: Int): IO[AppError, Int] = {
for {
result <- method(input)
_ <- IO.fail(AppError("app error")).when(result)
} yield input
}
but this code does not compile, because compiler says result type is:
ZIO[Any, Throwable, Int]
How to convert from Task (where I call method) to IO?
You'll need to decide what you want to do with Throwable errors which are not AppError.
If you decide you want to map them to an AppError you can do:
method(input).mapError {
case ae: AppError => ae
case other => AppError(other.getMessage)
}
If you want to refine those errors and only keep the ones that are AppError then you can use one of the refine* family of operators, which will keep errors that match the predicate and terminate the fiber otherwise.
method(input).refineToOrDie[AppError] // IO[AppError, Boolean]
// Or
method(input).refineOrDie { case ae: AppError => ae } // IO[AppError, Boolean]
Or if you want to assume that all errors from method are considered "Fiber terminating", then you can use .orDie to absorb the error and kill the fiber:
method(input).orDie // UIO[Boolean]
Or if you want to recover from the error and handle it a different way then you could use the catch* family
method(input).catchAll(_ => UIO.succeed(false)) // UIO[Boolean]
Finally if you wanted to have the result mapped into an Either you could use .either, which will lift the error out of the error channel and map it into Either[E, A]
method(input).either // UIO[Either[Throwable, Boolean]]
There is a great cheat sheet (though admittedly a bit out of date) here as well
I wanted to make a compose function for piping and im stuck. I managed to make a pointfree pipe but cant figure out composing.
// pointfree
const pipe = fn => future => future.pipe(fn)
// compose pipes // not working
const composePipe = (...fns) => (...args) => fns.reduceRight( (future, fn) => future.pipe(fn), args)[0];
I'll answer your question eventually, but let's take a step back first.
An important thing to understand is that the pipe method is just function application. In other terms: future.pipe (f) == f (future).
This means that your pipe function can be redefined as such:
const pipe = fn => future => future.pipe(fn)
//to:
const pipe = fn => value => fn (value)
This new version of pipe works exactly the same way, except that it works on any values, not just Futures. But let's take a step back further even.
The signature of this function is as follows: pipe :: (a -> b) -> a -> b. It takes a function from A to B, and returns a function from A to B.
Wait a minute....
const pipe = fn => value => fn (value)
//to:
const pipe = fn => fn
That new definition does the same thing. Except that it works on anything, not just Functions. Actually it's just the identity function. So a curried (you said point-free, but I think you meant curried) version of future.pipe is just the identity function.
So why is this? Because all .pipe does is function application. And you can apply your functions yourself.
Now to answer your next question about composing pipes. What you're actually looking for is something that takes a number of functions, and applies them in sequence.
If you're using Ramda, that's pipe. We can implement this ourselves though:
const pipe = (...fns) => (...args) => fns.reduce ((args, f) => [f (...args)], args)[0]
I get some list of data from a HTTP call. I then know what values to get for another HTTP call. I would like to have everything be asynchronous. But I need to use this data with Expecto's testCaseAsync : string -> Async<unit> -> Test. So, my goal is to get a signature like so Async<Item>[]
So, I would like to get a list of testCaseAsync.
So, I basically have something like this:
// Async<Async<Item>[]>
let getAsyncCalls =
async {
let! table = API.getTable ()
// Async<Item>[]
let items =
table.root
|> Array.map (fun x -> API.getItem x.id)
return item
}
If I run them in parallel I get:
// Async<Item[]>
let getAsyncCalls =
async {
let! table = API.getTable ()
// Item[]
let! items =
table.root
|> Array.map (fun x -> API.getItem x.id)
return item
}
So, that doesn't get me to Async<Item>[]. I'm not sure if this is possible. I would like to avoid Async.RunSynchronously for the API.getTable call since that can lead to deadlocks, right? It will most likely be called from a cached value (memoized) so I'm not sure that will make a difference.
I guess I'll keep working on it unless someone else is more clever than me :-) Thanks in advance!
In general, you cannot turn Async<Async<T>[]> into Async<T>[]. The problem is that to even get the length of the array, you need to perform some operation asynchronously, so there is no way to "lift" the array outside of the async. If you knew the length of the array in advance, then you can make this work.
The following function turns Async<'T[]> into Async<'T>[] provided that you give it the length of the array. As you figured out, the returned asyncs need to somehow share access to the one top-level async. The easiest way of doing this I can think of is to use a task. Adapting that for your use case should be easy:
let unwrapAsyncArray (asyncs:Async<'T[]>) len =
let task = asyncs |> Async.StartAsTask
Array.init len (fun i -> async {
let! res = Async.AwaitTask task
if res.Length <> len then failwith "Wrong length!"
return res.[i] }
)
Here are four functions I am trying to compose into a single endpoint string:
const endpoint = str => `${str}` || 'default'
const protocol = str => `https://${str}`
const params = str => `${str}?sort=desc&part=true&`
const query = str => `${str}query={ some:'value', another:'value'}`
let finalEndpoint = R.compose(query, params, protocol, endpoint)
var result = finalEndpoint('api.content.io')
This composition works and returns the result I want which is:
https://api.content.io?sort=desc&part=true&query={ some:'value', another:'value'}
But notice how I have hard coded the values for params and query inside their function body. I see only one value going up the value in this R.compose chain.
How and where exactly do I pass in parameters to the params and query parameters?
UPDATE:
What I did was curried those functions like this:
var R = require('ramda');
const endpoint = str => `${str}` || 'default'
const protocol = str => `https://${str}`
const setParams = R.curry ( (str, params) => `${str}?${params}` )
const setQuery = R.curry ( (str, query) => `${str}&query=${JSON.stringify(query)}` )
and then
let finalEndpoint = R.compose(protocol, endpoint)
var result = setQuery(setParams(finalEndpoint('api.content.io'), 'sort=desc&part=true'), { some:'value', another:'value'})
console.log(result);
But the final call to get result still seems pretty hacked and inelegant. Is there any way to improve this?
How and where exactly do I pass in parameters to the params and query parameters?
Honestly, you don't, not when you're building a compose or pipe pipeline with Ramda or similar libraries.
Ramda (disclaimer: I'm one of the authors) allows the first function to receive multiple arguments -- some other libraries do, some don't -- but subsequent ones will only receive the result of the previous calls. There is one function in Sanctuary, meld, which might be helpful with this, but it does have a fairly complex API.
However, I don't really understand why you are building this function in this manner in the first place. Are those intermediate functions actually reusable, or are you building them on spec? The reason I ask is that this seems a more sensible version of the same idea:
const finalEndpoint = useWith(
(endpoint, params, query) =>`https://${endpoint}?${params}&query=${query}`, [
endpoint => endpoint || 'default',
pipe(toPairs, map(join('=')), join('&')),
pipe(JSON.stringify, encodeURIComponent)
]
);
finalEndpoint(
'api.content.io',
{sort: 'desc', part: true},
{some:'value', another:'value'}
);
//=> "https://api.content.io?sort=desc&part=true&query=%7B%22some%22%3A%22value%22%2C%22another%22%3A%22value%22%7D"
I don't really know your requirements for that last parameter. It looked strange to me without that encodeUriComponent, but perhaps you don't need it. And I also took liberties with the second parameter, assuming that you would prefer actual data in the API to a string encapsulating that data. But if you want to pass 'sort=desc&part=true', then replace pipe(toPairs, map(join('=')), join('&')) with identity.
Since the whole thing is far from points-free, I did not use a points-free version of the first function, perhaps or(__, 'default'), as I think what's there is more readable.
Update
You can see a version of this on the Ramda REPL, one that adds some console.log statements with tap.
This does raise an interesting question for Ramda. If those intermediate functions really are desirable, Ramda offers no way to combine them. Obviously Ramda could offer something like meld, but is there a middle ground? I'm wondering if there is a useful function (curried, of course) that we should include that works something like
someFunc([f0], [a0]); //=> f0(a0)
someFunc([f0, f1], [a0, a1]); //=> f1(f0(a0), a1)
someFunc([f0, f1, f2], [a0, a1, a2]); //=> f2(f1(f0(a0), a1), a2)
someFunc([f0, f1, f2, f3], [a0, a1, a2, a3]); //=> f3(f2(f1(f0(a0), a1), a2), a3)
// ...
There are some serious objections: What if the lists are of different lengths? Why is the initial call unary, and should we fix that by adding a separate accumulator parameter to the function? Nonetheless, this is an intriguing function, and I will probably raise it for discussion on the Ramda boards.
I wrote a little helper function for situations like this.
It is like compose, but with the rest params also passed in. The first param is the return value of the previous function. The rest params remain unchanged.
With it, you could rewrite your code as follows:
const compound = require('compound-util')
const endpoint = str => `${str}` || 'default'
const protocol = str => `https://${str}`
const params = (str, { params }) => `${str}?${params}`
const query = (str, { query }) => `${str}query=${query}`
const finalEndpoint = compound(query, params, protocol, endpoint)
const result = finalEndpoint('api.content.io', {
params: 'sort=desc&part=true&',
query: JSON.stringify({ some:'value', another:'value'})
})
If you have params and query as curried functions then you can:
EDIT: code with all the bells and whistles, needed to change parameter order or use R.__ and stringify object
const endpoint = R.curry( str => `${str}` || 'default' )
const protocol = R.curry( str => `https://${str}` )
const params = R.curry( (p, str) => `${str}?${p}` )
const query = R.curry( (q, str) => `${str}&query=${q}` )
let finalEndpoint =
R.compose(
query(JSON.stringify({ some:'value', another:'value' })),
params('sort=desc&part=true'),
protocol,
endpoint
)
var result = finalEndpoint('api.content.io')
console.log(result)
Consider the following type:
declare class Test<T> {
static of(value: T): Test<T>;
map<U>(fn: (value:T) => U): Test<U>;
}
Now for function ap, T is a function and it works like this:
Test.of(x => x * 2)
.ap(Test.of(5))
.map(console.log) // Output(number): 10
Test.of(x => `${x * 2}!`)
.ap(Test.of(5))
.map(console.log) // Output(string): 10!
So, to properly type check ap I need to do ap(Test<[get type of x]>): [type of output of T]
I tried Test<I, O>, where I is optional for values. But, it adds a lots of unnecessary thing to other functions. Is there any better way to solve this?
Note: I'm trying to write type definition for data.task
This is a tricky one! The ap() method can't be called on an instance of Test<T> for all T's, but only when T is a function that takes at most one argument.
So what you really need is something that is still TODO for Flow. It would look like this:
declare class Test<T> {
static of(value: T): Test<T>;
map<U>(fn: (value:T) => U): Test<U>;
ap<I,O>(this: Test<(in: I) => O>, test: Test<I>): Test<O>;
}
It declares that this must be a Test<T> where T is a function that takes I. Here's a GitHub issue about it.
In the meantime, you could do a first order approximation. It would look like this:
declare class Test<T> {
static of<I, O>(fn: (in: I) => O): FuncTest<I, O>;
static of(value: T): Test<T>;
map<U>(fn: (value:T) => U): Test<U>;
}
declare class FuncTest<I, O> extends Test<(in: I) => O> {
ap(x: Test<I>): Test<O>;
}
Test.of(x => x * 2)
.ap(Test.of(5))
.map(x => (x: number)) // no error
Test.of(x => `${x * 2}!`)
.ap(Test.of(5))
.map(x => (x: string)) // no error
Try this example on flowtype.org/try
The downside of this approach is that ap() returns Test<O>, even if O is a function. So you can't call ap() twice.
Test.of(x => Test.of(y => x * y))
.ap(Test.of(5))
.map(x => (x: Test<(y: number) => number>)); // This is fine
Test.of(x => Test.of(y => x * y))
.ap(Test.of(5))
.ap(Test.of(2)) // This is an error :(