Skip to content

Fleshing out result methods #2455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 29, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 138 additions & 4 deletions src/libcore/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,79 @@ fn chain_err<T: copy, U: copy, V: copy>(
}
}

#[doc = "
Call a function based on a previous result

If `res` is `ok` then the value is extracted and passed to `op` whereupon
`op`s result is returned. if `res` is `err` then it is immediately returned.
This function can be used to compose the results of two functions.

Example:

iter(read_file(file)) { |buf|
print_buf(buf)
}
"]
fn iter<T, E>(res: result<T, E>, f: fn(T)) {
alt res {
ok(t) { f(t) }
err(_) { }
}
}

#[doc = "
Call a function based on a previous result

If `res` is `err` then the value is extracted and passed to `op` whereupon
`op`s result is returned. if `res` is `ok` then it is immediately returned.
This function can be used to pass through a successful result while handling
an error.
"]
fn iter_err<T, E>(res: result<T, E>, f: fn(E)) {
alt res {
ok(_) { }
err(e) { f(e) }
}
}

#[doc = "
Call a function based on a previous result

If `res` is `ok` then the value is extracted and passed to `op` whereupon
`op`s result is wrapped in `ok` and returned. if `res` is `err` then it is
immediately returned. This function can be used to compose the results of two
functions.

Example:

let res = map(read_file(file)) { |buf|
parse_buf(buf)
}
"]
fn map<T, E: copy, U: copy>(res: result<T, E>, op: fn(T) -> U)
-> result<U, E> {
alt res {
ok(t) { ok(op(t)) }
err(e) { err(e) }
}
}

#[doc = "
Call a function based on a previous result

If `res` is `err` then the value is extracted and passed to `op` whereupon
`op`s result is wrapped in an `err` and returned. if `res` is `ok` then it is
immediately returned. This function can be used to pass through a successful
result while handling an error.
"]
fn map_err<T: copy, E, F: copy>(res: result<T, E>, op: fn(E) -> F)
-> result<T, F> {
alt res {
ok(t) { ok(t) }
err(e) { err(op(e)) }
}
}

impl extensions<T:copy, E:copy> for result<T,E> {
fn get() -> T { get(self) }

Expand All @@ -123,6 +196,34 @@ impl extensions<T:copy, E:copy> for result<T,E> {
fn chain_err<F:copy>(op: fn(E) -> result<T,F>) -> result<T,F> {
chain_err(self, op)
}

fn iter(f: fn(T)) {
alt self {
ok(t) { f(t) }
err(_) { }
}
}

fn iter_err(f: fn(E)) {
alt self {
ok(_) { }
err(e) { f(e) }
}
}

fn map<U:copy>(op: fn(T) -> U) -> result<U,E> {
alt self {
ok(t) { ok(op(t)) }
err(e) { err(e) }
}
}

fn map_err<F:copy>(op: fn(E) -> F) -> result<T,F> {
alt self {
ok(t) { ok(t) }
err(e) { err(op(e)) }
}
}
}

#[doc = "
Expand All @@ -142,7 +243,7 @@ checking for overflow:
assert incd == [2u, 3u, 4u];
}
"]
fn map<T,U:copy,V:copy>(
fn map_vec<T,U:copy,V:copy>(
ts: [T], op: fn(T) -> result<V,U>) -> result<[V],U> {

let mut vs: [V] = [];
Expand Down Expand Up @@ -177,7 +278,7 @@ length. While we do not often use preconditions in the standard
library, a precondition is used here because result::t is generally
used in 'careful' code contexts where it is both appropriate and easy
to accommodate an error like the vectors being of different lengths."]
fn map2<S,T,U:copy,V:copy>(ss: [S], ts: [T], op: fn(S,T) -> result<V,U>)
fn map_vec2<S,T,U:copy,V:copy>(ss: [S], ts: [T], op: fn(S,T) -> result<V,U>)
: vec::same_length(ss, ts) -> result<[V],U> {

let n = vec::len(ts);
Expand All @@ -199,8 +300,8 @@ Applies op to the pairwise elements from `ss` and `ts`, aborting on
error. This could be implemented using `map2()` but it is more efficient
on its own as no result vector is built.
"]
fn iter2<S,T,U:copy>(ss: [S], ts: [T],
op: fn(S,T) -> result<(),U>)
fn iter_vec2<S,T,U:copy>(ss: [S], ts: [T],
op: fn(S,T) -> result<(),U>)
: vec::same_length(ss, ts)
-> result<(),U> {

Expand Down Expand Up @@ -248,4 +349,37 @@ mod tests {
fn chain_failure() {
assert get_err(chain(op3(), op2)) == "sadface";
}

#[test]
fn test_impl_iter() {
let mut valid = false;
ok::<str, str>("a").iter { |_x| valid = true; };
assert valid;

err::<str, str>("b").iter { |_x| valid = false; };
assert valid;
}

#[test]
fn test_impl_iter_err() {
let mut valid = true;
ok::<str, str>("a").iter_err { |_x| valid = false; };
assert valid;

valid = false;
err::<str, str>("b").iter_err { |_x| valid = true; };
assert valid;
}

#[test]
fn test_impl_map() {
assert ok::<str, str>("a").map { |_x| "b" } == ok("b");
assert err::<str, str>("a").map { |_x| "b" } == err("a");
}

#[test]
fn test_impl_map_err() {
assert ok::<str, str>("a").map_err { |_x| "b" } == ok("a");
assert err::<str, str>("a").map_err { |_x| "b" } == err("b");
}
}
105 changes: 105 additions & 0 deletions src/libstd/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export to_str;
export from_reader;
export from_str;
export eq;
export to_json;

export num;
export string;
Expand Down Expand Up @@ -498,6 +499,110 @@ fn eq(value0: json, value1: json) -> bool {
}
}

iface to_json { fn to_json() -> json; }

impl of to_json for json {
fn to_json() -> json { self }
}

impl of to_json for i8 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for i16 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for i32 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for i64 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for u8 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for u16 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for u32 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for u64 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for float {
fn to_json() -> json { num(self) }
}

impl of to_json for f32 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for f64 {
fn to_json() -> json { num(self as float) }
}

impl of to_json for () {
fn to_json() -> json { null }
}

impl of to_json for bool {
fn to_json() -> json { boolean(self) }
}

impl of to_json for str {
fn to_json() -> json { string(self) }
}

impl <A: to_json copy, B: to_json copy> of to_json for (A, B) {
fn to_json() -> json {
let (a, b) = self;
list([a.to_json(), b.to_json()])
}
}

impl <A: to_json copy, B: to_json copy, C: to_json copy>
of to_json for (A, B, C) {
fn to_json() -> json {
let (a, b, c) = self;
list([a.to_json(), b.to_json(), c.to_json()])
}
}

impl <A: to_json> of to_json for [A] {
fn to_json() -> json { list(self.map { |elt| elt.to_json() }) }
}

impl <A: to_json copy> of to_json for hashmap<str, A> {
fn to_json() -> json {
let d = map::str_hash();
for self.each() { |key, value|
d.insert(key, value.to_json());
}
dict(d)
}
}

impl <A: to_json> of to_json for option<A> {
fn to_json() -> json {
alt self {
none { null }
some(value) { value.to_json() }
}
}
}

impl of to_str::to_str for json {
fn to_str() -> str { to_str(self) }
}

#[cfg(test)]
mod tests {
fn mk_dict(items: [(str, json)]) -> json {
Expand Down
16 changes: 10 additions & 6 deletions src/rustc/middle/typeck/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ import middle::ty::{ty_vid, tys_in_fn_ty, region_vid, vid};
import syntax::{ast, ast_util};
import syntax::ast::{ret_style};
import util::ppaux::{ty_to_str, mt_to_str};
import result::{result, extensions, ok, err, map, map2, iter2};
import result::{result, extensions, ok, err, map_vec, map_vec2, iter_vec2};
import ty::{mk_fn, type_is_bot};
import check::regionmanip::{collect_bound_regions_in_tys,
replace_bound_regions};
Expand Down Expand Up @@ -753,7 +753,7 @@ impl unify_methods for infer_ctxt {
as: [@ty::type_constr], bs: [@ty::type_constr]) -> ures {

if check vec::same_length(as, bs) {
iter2(as, bs) {|a,b|
iter_vec2(as, bs) {|a,b|
self.constrs(a, b)
}
} else {
Expand Down Expand Up @@ -1237,7 +1237,9 @@ fn super_tps<C:combine>(
// variance.

if check vec::same_length(as, bs) {
iter2(as, bs) {|a, b| self.infcx().eq_tys(a, b) }.then {||
iter_vec2(as, bs) {|a, b|
self.infcx().eq_tys(a, b)
}.then {||
ok(as)
}
} else {
Expand Down Expand Up @@ -1331,7 +1333,7 @@ fn super_fns<C:combine>(
self: C, a_args: [ty::arg], b_args: [ty::arg]) -> cres<[ty::arg]> {

if check vec::same_length(a_args, b_args) {
map2(a_args, b_args) {|a, b| self.args(a, b) }
map_vec2(a_args, b_args) {|a, b| self.args(a, b) }
} else {
err(ty::terr_arg_count)
}
Expand Down Expand Up @@ -1469,7 +1471,9 @@ fn super_tys<C:combine>(

(ty::ty_rec(as), ty::ty_rec(bs)) {
if check vec::same_length(as, bs) {
map2(as, bs) {|a,b| self.flds(a, b) }.chain {|flds|
map_vec2(as, bs) {|a,b|
self.flds(a, b)
}.chain {|flds|
ok(ty::mk_rec(tcx, flds))
}
} else {
Expand All @@ -1479,7 +1483,7 @@ fn super_tys<C:combine>(

(ty::ty_tup(as), ty::ty_tup(bs)) {
if check vec::same_length(as, bs) {
map2(as, bs) {|a, b| self.tys(a, b) }.chain {|ts|
map_vec2(as, bs) {|a, b| self.tys(a, b) }.chain {|ts|
ok(ty::mk_tup(tcx, ts))
}
} else {
Expand Down