Excuse me the lengthy example:
module type MONAD = sig
type ('r, 'a) t
val return : 'a -> ('r, 'a) t
val bind : ('r, 'a) t -> ('a -> ('r, 'b) t) -> ('r, 'b) t
end
module MonadOps (Monad : MONAD) = struct
include Monad
type ('r, 'a) monad = ('r, 'a) t
let run x = x
let return = Monad.return
let bind = Monad.bind
let (>>=) a b = bind a b
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
let whenM p s = if p then s else return ()
let lift f m = perform x <-- m; return (f x)
let join m = perform x <-- m; x
let (>=>) f g = fun x -> f x >>= g
end
module Monad = (MonadOps : functor (M : MONAD) -> sig
type ('a, 'b) monad
val run : ('a, 'b) monad -> ('a, 'b) M.t
val return : 'a -> ('b, 'a) monad
val bind : ('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
val ( >>= ) :
('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
val foldM :
('a -> 'b -> ('c, 'a) monad) -> 'a -> 'b list -> ('c, 'a) monad
val whenM : bool -> ('a, unit) monad -> ('a, unit) monad
val lift : ('a -> 'b) -> ('c, 'a) monad -> ('c, 'b) monad
val join : ('a, ('a, 'b) monad) monad -> ('a, 'b) monad
val ( >=> ) :
('a -> ('b, 'c) monad) ->
('c -> ('b, 'd) monad) -> 'a -> ('b, 'd) monad
end)
module type MONAD_PLUS = sig
include MONAD
val mzero : ('r, 'a) t
val mplus : ('r, 'a) t -> ('r, 'a) t -> ('r, 'a) t
end
module MonadPlusOps (MonadPlus : MONAD_PLUS) = struct
include MonadOps (MonadPlus)
let mzero = MonadPlus.mzero
let mplus = MonadPlus.mplus
let fail = mzero
let (++) a b = mplus a b
let guard p = if p then return () else fail
end
Is there a way to have MonadPlus analogous to Monad without excessive signature code duplication? Along the lines of (wrong solution):
module MonadPlus = (MonadPlusOps : functor (M : MONAD_PLUS) -> sig
include module type of MonadPlusOps (M)
with type ('a, 'b) t := ('a, 'b) MonadPlusOps (M).monad
end)
or (does not type-check):
module MonadPlus = (MonadPlusOps : functor (M : MONAD_PLUS) -> sig
include module type of Monad(M)
val mzero : ('a, 'b) monad
(* ... *)
end)
Edit: updated -- better final solution
module type MONAD = sig
type ('s, 'a) t
val return : 'a -> ('s, 'a) t
val bind : ('s, 'a) t -> ('a -> ('s, 'b) t) -> ('s, 'b) t
end
module type MONAD_OPS = sig
type ('s, 'a) monad
include MONAD with type ('s, 'a) t := ('s, 'a) monad
val ( >>= ) :
('s, 'a) monad -> ('a -> ('s, 'b) monad) -> ('s, 'b) monad
val foldM :
('a -> 'b -> ('s, 'a) monad) -> 'a -> 'b list -> ('s, 'a) monad
val whenM : bool -> ('s, unit) monad -> ('s, unit) monad
val lift : ('a -> 'b) -> ('s, 'a) monad -> ('s, 'b) monad
val join : ('s, ('s, 'a) monad) monad -> ('s, 'a) monad
val ( >=> ) :
('a -> ('s, 'b) monad) ->
('b -> ('s, 'c) monad) -> 'a -> ('s, 'c) monad
end
module MonadOps (M : MONAD) = struct
open M
type ('s, 'a) monad = ('s, 'a) t
let run x = x
let (>>=) a b = bind a b
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
let whenM p s = if p then s else return ()
let lift f m = perform x <-- m; return (f x)
let join m = perform x <-- m; x
let (>=>) f g = fun x -> f x >>= g
end
module Monad (M : MONAD) =
sig
include MONAD_OPS
val run : ('s, 'a) monad -> ('s, 'a) M.t
end = struct
include M
include MonadOps(M)
end
module type MONAD_PLUS = sig
include MONAD
val mzero : ('s, 'a) t
val mplus : ('s, 'a) t -> ('s, 'a) t -> ('s, 'a) t
end
module type MONAD_PLUS_OPS = sig
include MONAD_OPS
val mzero : ('s, 'a) monad
val mplus : ('s, 'a) monad -> ('s, 'a) monad -> ('s, 'a) monad
val fail : ('s, 'a) monad
val (++) : ('s, 'a) monad -> ('s, 'a) monad -> ('s, 'a) monad
val guard : bool -> ('s, unit) monad
end
module MonadPlus (M : MONAD_PLUS) :
sig
include MONAD_PLUS_OPS
val run : ('s, 'a) monad -> ('s, 'a) M.t
end = struct
include M
include MonadOps(M)
let fail = mzero
let (++) a b = mplus a b
let guard p = if p then return () else fail
end
I'm not entirely sure what you are trying to achieve, but I would perhaps try to factor it as follows:
module type MONAD =
sig
type ('r, 'a) t
val return : 'a -> ('r, 'a) t
val bind : ('r, 'a) t -> ('a -> ('r, 'b) t) -> ('r, 'b) t
end
module type MONAD_OPS =
sig
type ('a, 'b) monad
val run : ('a, 'b) monad -> ('a, 'b) monad
val (>>=) : ('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
(* ... *)
end
module MonadOps (Monad : MONAD) :
sig
include MONAD with type ('a ,'b) t := ('a, 'b) Monad.t
include MONAD_OPS with type ('a ,'b) monad = ('a, 'b) Monad.t
end =
struct
include Monad
type ('r, 'a) monad = ('r, 'a) t
let run x = x
let (>>=) = bind
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
(* ... *)
end
module type MONAD_PLUS = sig
include MONAD
val mzero : ('r, 'a) t
val mplus : ('r, 'a) t -> ('r, 'a) t -> ('r, 'a) t
end
module type MONAD_PLUS_OPS =
sig
include MONAD_OPS
val fail : ('r, 'a) monad
val (++) : ('r, 'a) monad -> ('r, 'a) monad -> ('r, 'a) monad
(* ... *)
end
module MonadPlusOps (MonadPlus : MONAD_PLUS) :
sig
include MONAD_PLUS with type ('a ,'b) t := ('a, 'b) Monad.t
include MONAD_PLUS_OPS with type ('a ,'b) monad = ('a, 'b) Monad.t
end =
struct
include MonadPlus
include MonadOps (MonadPlus)
let fail = mzero
let (++) = mplus
(* ... *)
end
As a complement to Andreas' answer, I wished to show that you can use functors to produce signatures. I haven't exactly followed the discussion on which exact level of type abstraction you want, so this code is to be compared with Andreas' version.
module MonadSig = struct
module type S = sig
type ('r, 'a) t
val return : 'a -> ('r, 'a) t
val bind : ('r, 'a) t -> ('a -> ('r, 'b) t) -> ('r, 'b) t
end
end
module MonadOpsSig (M : MonadSig.S) = struct
module type S = sig
type ('a, 'b) monad = ('a, 'b) M.t
val run : ('a, 'b) monad -> ('a, 'b) monad
val (>>=) : ('a, 'b) monad -> ('b -> ('a, 'c) monad) -> ('a, 'c) monad
(* ... *)
end
end
module MonadOps (M : MonadSig.S) : MonadOpsSig(M).S = struct
open M
type ('r, 'a) monad = ('r, 'a) t
let run x = x
let (>>=) = bind
let rec foldM f a = function
| [] -> return a
| x::xs -> f a x >>= fun a' -> foldM f a' xs
(* ... *)
end
module MonadPlusSig = struct
module type S = sig
include MonadSig.S
val mzero : ('r, 'a) t
val mplus : ('r, 'a) t -> ('r, 'a) t -> ('r, 'a) t
end
end
module MonadPlusOpsSig (Monad : MonadPlusSig.S) = struct
module type S = sig
include MonadOpsSig(Monad).S
val fail : ('r, 'a) monad
val (++) : ('r, 'a) monad -> ('r, 'a) monad -> ('r, 'a) monad
(* ... *)
end
end
module MonadPlusOps (M : MonadPlusSig.S) : MonadPlusOpsSig(M).S = struct
include MonadOps(M)
open M
let fail = mzero
let (++) = mplus
(* ... *)
end
The idea is that to provide a signature parametrized on something, you can either embed this signature into a parametrized functor (I'd call this the "functor style"), or define the parameters as abstract (but they're really inputs rather than outputs) and, at use site, equate them with the actual parameters (I'd call this the "mixin style"). I'm not saying the code above is better than Andreas', in fact I'd probably rather use his version, but its interesting to compare them.
Related
I have just started programming a heterogeneous key/value collection to better understand proofs. Here is my code:
data Collection : Type where
None : Collection
Cons : {a : Type} ->
(name : String) ->
(value : a) ->
Collection ->
Collection
example : Collection
example = Cons "str" "tralala" (Cons {a = Int} "num" 1 None)
data AllValues : (p : a -> Type) -> Collection -> Type where
First : AllValues p None
Next : p a => AllValues p col -> AllValues p (Cons {a} k v col)
printCollection : (col : Collection) -> {auto prf : AllValues Show col} -> String
printCollection None {prf = First} = "."
printCollection (Cons key value rest) {prf = (Next later)} =
"(" ++ key ++ ": " ++ show value ++ "), " ++ printCollection rest
--------------------------------------------------------------------------------
data IsNewKey : (key : String) -> Collection -> Type where
U1 : IsNewKey key None
U2 : Not (key = k) => IsNewKey key col -> IsNewKey key (Cons k value col)
insert : {a : Type} ->
(name : String) ->
(value : a) ->
{auto notIn : IsNewKey name col} ->
(col : Collection) ->
Collection
insert {a} name value col = Cons {a} name value col
data IsElement : String -> Collection -> Type where
Here : IsElement key (Cons key value rest)
There : (later : IsElement key rest) -> IsElement key (Cons name value rest)
update : (key : String) ->
(newVal : ty) ->
(col : Collection) ->
{auto prf : IsElement key col} ->
Collection
update key newVal (Cons key _ rest) {prf = Here} = Cons key newVal rest
update key newVal (Cons name value rest) {prf = (There later)} =
Cons name value (update key newVal rest)
GetType : (key : String) -> (col : Collection) -> {auto prf : IsElement key col} -> Type
GetType key (Cons {a} key _ _) {prf = Here} = a
GetType key (Cons _ _ rest) {prf = There later} = GetType key rest
get : (key : String) -> (col : Collection) -> {auto prf : IsElement key col} -> GetType key col
get key (Cons key value _) {prf = Here} = value
get key (Cons _ _ rest) {prf = There later} = get key rest
I have two problems here:
Somehow Not (key = k) does not work, I am able to insert duplicated
keys.
How to derive an instance for the Show? Where to put All
Show...? (Probably it is not possible)
Any ideas?
OK, finally I was able to fix insert function with an advice from michaelmesser.
getKeys : Collection -> List String
getKeys None = []
getKeys (Cons name _ rest) = name :: getKeys rest
fromFalse : (d : Dec p) -> {auto isFalse : decAsBool d = False} -> Not p
fromFalse (Yes _) {isFalse = Refl} impossible
fromFalse (No contra) = contra
data NotEq : a -> a -> Type where
MkNotEq : {a : t} -> {b : t} -> Not (a = b) -> NotEq a b
%hint
notEq : DecEq t => {a : t} -> {b : t} -> {auto isFalse : decAsBool (decEq a b) = False} -> NotEq a b
notEq = MkNotEq (fromFalse (decEq _ _))
insert : {a : Type} ->
(name : String) ->
(value : a) ->
(col : Collection) ->
{auto prf : All (NotEq name) (getKeys col)} ->
Collection
insert {a} name value col = Cons {a} name value col
I also added another useful functions: delete, decompose, hasKey, upsert.
infixr 7 .::
(.::) : (String, a) -> (col : Collection) -> Collection
(.::) (name, value) col = Cons name value col
delete : (key : String) ->
(col : Collection) ->
{auto prf : IsElement key col} ->
Collection
delete key (Cons key _ rest) {prf = Here} = rest
delete key (Cons name value rest) {prf = (There later)} =
Cons name value (delete key rest)
HeadType : Collection -> Type
HeadType None = Maybe ()
HeadType (Cons {a} _ _ _) = a
data NotEmpty : Collection -> Type where
MkNotEmpty : NotEmpty (Cons k v r)
decompose : (col : Collection) -> {auto prf : NotEmpty col} -> (HeadType col, Collection)
decompose (Cons k v r) {prf = MkNotEmpty} = (v, r)
notInNone : IsElement key None -> Void
notInNone Here impossible
notInNone (There _) impossible
notInTail : (notThere : IsElement key rest -> Void) ->
(notHere : (key = name) -> Void) ->
IsElement key (Cons name value rest) -> Void
notInTail _ notHere Here = notHere Refl
notInTail notThere _ (There later) = notThere later
hasKey : (key : String) -> (col : Collection) -> Dec (IsElement key col)
hasKey key None = No notInNone
hasKey key (Cons name value rest) =
case key `decEq` name of
Yes Refl => Yes Here
No notHere =>
case hasKey key rest of
Yes later => Yes (There later)
No notThere => No (notInTail notThere notHere)
upsert : {a : Type} ->
(name : String) ->
(value : a) ->
(col : Collection) ->
Collection
upsert name value col =
case hasKey name col of
Yes _ => update name value col
No _ => (name, value) .:: col
Why won't this compile?
trait T {}
fn f<U: 'static + T, V, E>(f2: V) -> impl Fn() -> Result<Box<dyn T>, E>
where
V: Fn() -> Result<U, E>,
{
move || -> Result<Box<dyn T>, E> { f2().map(Box::new) }
}
The error message is:
error[E0308]: mismatched types
--> src/lib.rs:7:40
|
7 | move || -> Result<Box<dyn T>, E> { f2().map(Box::new) }
| ^^^^^^^^^^^^^^^^^^ expected trait T, found type parameter
|
= note: expected type `std::result::Result<std::boxed::Box<(dyn T + 'static)>, _>`
found type `std::result::Result<std::boxed::Box<U>, _>`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
This version is ok:
trait T {}
fn f<U: 'static + T, V, E>(f2: V) -> impl Fn() -> Result<Box<dyn T>, E>
where
V: Fn() -> Result<U, E>,
{
move || -> Result<Box<dyn T>, E> {
match f2() {
Ok(result) => Ok(Box::new(result)),
Err(e) => Err(e),
}
}
}
In my opinion, (dyn T + 'static) and U are the same; am I right?
I'm using rustc 1.39.0-nightly (f0b58fcf0 2019-09-11).
It's a limitation and I don't know if it will compile one day. The reason is that Rust doesn't know to convert between the two Result types, (dyn T + 'static) and U are totally different things. If this acceptable you can do f2().map(|x| Box::new(x) as _).
The cast will allow the compiler to transform U into (dyn T + 'static) before put it in the result, we don't need to explicit the cast type the compiler inference will do it for us (on most case).
A trait object can be obtained from a pointer to a concrete type that implements the trait by casting it (e.g. &x as &Foo)
See dynamic dispatch section of the book (didn't find any information in the new book).
Because of reasons, I want to define a generic function that can iterate over key-value pairs expressed either as a mapping, or as a vector of 2-tuples (or anything else that satisfies IntoIterator<Item=(K, V)>, where K and V are stringy). Concretely, I want this to work:
use std::collections::HashMap;
fn main() {
let vc = vec![
("a", "foo"),
("b", "bar"),
("c", "baz")
];
operate(&vc);
let mut map = HashMap::new();
map.insert("d", "blurf");
map.insert("e", "quux");
map.insert("f", "xyzzy");
operate(&map);
}
I've got a definition of operate that works for the HashMap, but not for the vector:
fn operate<I, K, V>(x: I)
where I: IntoIterator<Item=(K, V)>,
K: AsRef<str>, V: AsRef<str>
{
for (ref k, ref v) in x {
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
The error message I get is
error[E0271]: type mismatch resolving `<&std::vec::Vec<(&str, &str)> as std::iter::IntoIterator>::Item == (_, _)`
--> test.rs:18:5
|
18 | operate(&vc);
| ^^^^^^^ expected reference, found tuple
|
= note: expected type `&(&str, &str)`
= note: found type `(_, _)`
= note: required by `operate`
and I don't understand it at all. For one thing, it seems like it's backwards, and for another, why am I only getting an error for the Vec and not the HashMap?
The function provided by IntoIterator consumes self.
fn into_iter(self) -> Self::IntoIter
In order to allow the use of IntoIterator without consuming the collection, both Vec and HashMap have implementations of IntoIterator for &'a Vec<T> and &'a HashMap<K,V,S>, respectively. However, they are not quite the same.
For the hash map, each Item is a (&K, &V), which does not impose a problem because the code effectively assumes the items as 2-sized tuples of keys and values that coerce to &str. And &&str does indeed coerce to &str.
For the vector, each Item is a &T (thus &(K, V) in this case), but because the function is expecting (K, V) as the iterating item, it is currently unable to deal with items of &(K, V).
As it is, the function works if you move the vector, which yields an IntoIterator where Item = (K, V):
let vc = vec![
("a", "foo"),
("b", "bar"),
("c", "baz")
];
operate(vc);
But what if we want it to work for both collections without consuming any of them? Well, I just devised two solutions.
#1
This one involves hiding the tuple behind a new trait:
/// for stuff that can be turned into a pair of references
trait AsRefPair<K, V> {
fn as_ref_pair(&self) -> (&K, &V);
}
Implementing it for for &(K,V) and (&K,&V):
impl<'a, K, V> AsRefPair<K, V> for (&'a K, &'a V) {
fn as_ref_pair(&self) -> (&K, &V) {
(self.0, self.1)
}
}
impl<'a, K, V> AsRefPair<K, V> for &'a (K, V) {
fn as_ref_pair(&self) -> (&K, &V) {
(&self.0, &self.1)
}
}
And now this function works:
fn operate<I, T, K, V>(x: I)
where I: IntoIterator<Item=T>,
T: AsRefPair<K, V>,
K: AsRef<str>, V: AsRef<str>
{
for p in x {
let (ref k, ref v) = p.as_ref_pair();
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
Playground. It might sound a bit crazy at first, but...!
#2
In this one, just stop working with tuples... and start working with key-values!
trait KeyValue<K, V> {
fn key_value(&self) -> (&K, &V) {
(self.key(), self.value())
}
fn key(&self) -> &K;
fn value(&self) -> &V;
}
impl<K, V> KeyValue<K, V> for (K, V) {
fn key(&self) -> &K {
&self.0
}
fn value(&self) -> &V {
&self.1
}
}
impl<'a, K, V> KeyValue<K, V> for &'a (K, V) {
fn key(&self) -> &K {
&self.0
}
fn value(&self) -> &V {
&self.1
}
}
fn operate<I, T, K, V>(x: I)
where I: IntoIterator<Item=T>,
T: KeyValue<K, V>,
K: AsRef<str>, V: AsRef<str>
{
for p in x {
let (ref k, ref v) = p.key_value();
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
Playground. I find this one a bit more idiomatic.
If you pass to the function operate() an iterator instead of a reference to vector, you can use Iterator adaptors to convert Iterator::Item to what you need:
operate(vc.iter().map(|&(ref a, ref b)| (a, b)));
Here I have a vector vec of (char, usize) pairs, and I want to write a function
fn take_lt(&'a vec, cutoff: usize) -> Iterator<'a, char>
which returns an iterator over the chars matching values less than cutoff.
Is there a way to do this without the overhead of allocating something to heap (ie boxing up a Fn environment or creating another vector)?
Is there a way to do this
without having to explicitly write out the hideous associated return
type?
After trying this many different ways (a couple of which compiled, but all of which involved heap allocation which I'd like to avoid), I came up with:
use std::iter::repeat;
use std::iter::FilterMap;
use std::iter::Zip;
use std::iter::Repeat;
use std::slice;
fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, &fn((&(char, usize), usize)) -> Option<char>> {
fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
if a < b {
Some(x)
} else {
None
}
}
vec.iter().zip(repeat(cutoff)).filter_map(&cmp_fun)
}
This is close, but I get:
src/lib.rs:15:47: 15:55 error: mismatched types:
expected `&fn((&(char, usize), usize)) -> core::option::Option<char>`,
found `&fn((&(char, usize), usize)) -> core::option::Option<char> {take_lt::cmp_fun}`
(expected fn pointer,
found fn item) [E0308]
src/lib.rs:15 vec.iter().zip(repeat(cutoff)).filter_map(&cmp_fun)
^~~~~~~~
A little googling suggests I try casting the function item to a function pointer, as in:
vec.iter().zip(repeat(cutoff)).filter_map(&(cmp_fun as fn((&(char, usize), usize)) -> Option<char>))
but that fails with:
src/lib.rs:15:49: 15:103 error: borrowed value does not live long enough
src/lib.rs:15 vec.iter().zip(repeat(cutoff)).filter_map(&(cmp_fun as fn((&(char, usize), usize)) -> Option<char>))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/lib.rs:7:167: 16:2 note: reference must be valid for the lifetime 'a as defined on the block at 7:166...
src/lib.rs: 7 fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, &fn((&(char, usize), usize)) -> Option<char>> {
src/lib.rs: 8 fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
src/lib.rs: 9 if a < b {
src/lib.rs:10 Some(x)
src/lib.rs:11 } else {
src/lib.rs:12 None
...
src/lib.rs:7:167: 16:2 note: ...but borrowed value is only valid for the block at 7:166
src/lib.rs: 7 fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, &fn((&(char, usize), usize)) -> Option<char>> {
src/lib.rs: 8 fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
src/lib.rs: 9 if a < b {
src/lib.rs:10 Some(x)
src/lib.rs:11 } else {
src/lib.rs:12 None
...
You were close:
// type alias for the return type (optional, I just find it a bit
// optically easier to work with). I added:
// a 'a lifetime parameter that ties the return Iter lifetime to the
// input slice
// a 'static lifetime for the function pointer
type RetTake<'a> = FilterMap<Zip<slice::Iter<'a, (char, usize)>,
Repeat<usize>>, &'static fn((&(char, usize), usize)) -> Option<char>>;
fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> RetTake {
fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
if a < b {
Some(x)
} else {
None
}
}
// I think this explicit static binding
// used not to be necessary, but I now can't get rustc
// to give the reference to the function pointer the static lifetime
// it needs otherwise
static F: fn((&(char, usize), usize)) -> Option<char> = cmp_fun;
vec.iter().zip(repeat(cutoff)).filter_map(&F)
}
as an alternative you can create your own struct implementing the iterator logic you need and returning that struct. For instance:
struct CutoffIterator<'a> {
iter: slice::Iter<'a, (char, usize)>,
cutoff: usize,
}
impl<'a> Iterator for CutoffIterator<'a> {
type Item = char;
fn next(&mut self) -> Option<char> {
loop {
match self.iter.next() {
Some(&(x, a)) if a < self.cutoff => return Some(x),
Some(&(_, a)) if a >= self.cutoff => continue,
_ => return None
}
}
}
}
fn take_lt2(vec: &[(char, usize)], cutoff: usize) -> CutoffIterator {
CutoffIterator { iter: vec.iter(), cutoff: cutoff }
}
Each function has a unique, distinct type, that is compatible with an fn type. This mirrors the fact that closures also have distinct types. This is what the compiler means by found fn item: it didn't find the fn type you specified in the return type, but rather the unique type of the cmp_fun function.
fn types are already pointers, so there's no need (at least in your situation) to take a reference to an fn; you can just take an fn directly. By doing this, the compiler will implicitly cast the function to the more general fn type.
fn take_lt<'a>(vec: &'a[(char, usize)], cutoff: usize) -> FilterMap<Zip<slice::Iter<'a, (char, usize)>, Repeat<usize>>, fn((&(char, usize), usize)) -> Option<char>> {
fn cmp_fun((&(x, a), b): (&(char, usize), usize)) -> Option<char> {
if a < b {
Some(x)
} else {
None
}
}
vec.iter().zip(repeat(cutoff)).filter_map(cmp_fun)
}
I want to write an Iterator adaptor which applies a function recursively to its underlying Iterator. Recursively because the variant IR::Loop includes a Vec<IR>, of which an iterator should also be passed to the function.
The function should take an &mut Iterator<Item = IR> and use it to compute the next value of the iterator, (like itertools::batching).
use std::iter::Peekable;
#[derive(Clone)]
enum IR {
OperationA,
OperationB,
Loop(Vec<IR>),
}
pub trait MyItertools: Iterator {
fn apply_recursive<F: Fn(&mut Peekable<Self>) -> Option<Self::Item>>(
self,
f: F,
) -> ApplyRecursive<Self, F>
where
Self: Sized,
Self::Item: Clone,
{
ApplyRecursive {
iter: self.peekable(),
f: f,
}
}
}
impl<T: ?Sized> MyItertools for T
where
T: Iterator,
{
}
//applies a function recursively to some Iterator with Item=IR
#[derive(Clone)]
struct ApplyRecursive<I, F>
where
I: Iterator,
I::Item: Clone,
{
iter: Peekable<I>,
f: F,
}
impl<I: Iterator<Item = IR>, F> Iterator for ApplyRecursive<I, F>
where
F: Fn(&mut Peekable<I>)
-> Option<I::Item>,
{
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
match self.iter.peek() {
Some(&IR::Loop(code)) => {
self.iter.next(); //advance the iterator
let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();
Some(IR::Loop(code))
}
Some(x) => (self.f)(&mut self.iter),
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
fn main() {}
playground
What am I doing wrong? I don't even understand the error message:
error[E0277]: the trait bound `for<'r> F: std::ops::Fn<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not satisfied
--> src/main.rs:54:54
|
54 | let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();
| ^^^^^^^^^^^^^^^ the trait `for<'r> std::ops::Fn<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not implemented for `F`
|
= help: consider adding a `where for<'r> F: std::ops::Fn<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` bound
error[E0277]: the trait bound `for<'r> F: std::ops::FnOnce<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not satisfied
--> src/main.rs:54:54
|
54 | let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();
| ^^^^^^^^^^^^^^^ the trait `for<'r> std::ops::FnOnce<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not implemented for `F`
|
= help: consider adding a `where for<'r> F: std::ops::FnOnce<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` bound
error: no method named `collect` found for type `ApplyRecursive<std::vec::IntoIter<IR>, F>` in the current scope
--> src/main.rs:54:78
|
54 | let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();
| ^^^^^^^
|
= note: the method `collect` exists but the following trait bounds were not satisfied: `F : std::ops::Fn<(&mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>`, `ApplyRecursive<std::vec::IntoIter<IR>, F> : std::iter::Iterator`
= help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `collect`, perhaps you need to implement it:
= help: candidate #1: `std::iter::Iterator`
The last error indicates that you don't have an Iterator. Iterator is only implemented for your struct under certain conditions, and you aren't meeting them. The second error explains why.
the trait for<'r> Fn<(&'r mut IntoIter<IR>,)> is not implemented for the type F
So, why does the compiler think this won't work? Let's look at your constraints:
impl<I, F> Iterator for ApplyRecursive<I, F>
where
I: Iterator<Item = IR>
F: Fn(&mut Peekable<I>) -> Option<I::Item>,
This structure refers to a concrete type I that implements Iterator. Then F is a concrete type that accepts a mutable reference to the same concrete type as I. However, you try to use your function (specialized for whatever type it happens to be) on the concrete type IntoIter - but this might be a different concrete type!
The easiest fix is to remove the generics here:
impl<F> Iterator for ApplyRecursive<vec::IntoIter<IR>, F>
where
F: Fn(&mut vec::IntoIter<IR>) -> Option<IR>,
{
type Item = IR;
fn next(&mut self) -> Option<IR> {
This unlocks a whole other slew of errors about mutability, accessing private fields, and exporting private types, but I think it gets over this hump.
Alternatively, we can change F to accept a trait object, and not worry about specializing it:
pub trait CustomIter: Iterator {
fn apply_recursive<F>(self, f: F) -> ApplyRecursive<Self, F>
where
F: Fn(&mut Iterator<Item = Self::Item>) -> Option<Self::Item>,
Self: Sized,
Self::Item: Clone,
{
ApplyRecursive { iter: self.peekable(), f: f }
}
}
impl<I, F> Iterator for ApplyRecursive<I, F>
where
I: Iterator<Item = IR>,
F: Fn(&mut Iterator<Item = IR>) -> Option<IR>,
{
type Item = I::Item;
fn next(&mut self) -> Option<IR> {