Skip to content

Instantly share code, notes, and snippets.

@willowell
Last active July 11, 2020 02:25
Show Gist options
  • Save willowell/fe7ee35e8069661523331276a6cb50c9 to your computer and use it in GitHub Desktop.
Save willowell/fe7ee35e8069661523331276a6cb50c9 to your computer and use it in GitHub Desktop.
trouble translating Haskell functions to Rust
The Haskell functions I am trying to translate:
promptLine :: String -> IO String
promptLine msg = do
putStr msg
hFlush stdout
getLine
input :: Read a => String -> (a -> Bool) -> IO a
input msg validator = do
str <- promptLine msg
case (readMaybe :: Read a => String -> Maybe a) str of
Just x -> case validator x of
True -> return x
False -> do
putStrLn "Invalid input."
input msg validator
Nothing -> do
putStrLn "Invalid input."
input msg validator
-------------------------------------------------------------------------
What I have so far in Rust:
// I am deliberately using Option rather than Result here because
// I only need to worry about the type Option is holding and because
// Option and Result are both monads, so I figure my logic and function chaining should hold.
// Once these work with Option, I'm going to move them over to Result.
fn prompt_maybe(msg: &str) -> Option<String> {
use std::io::Write;
print!("{}", msg);
// Force output to stdout before reading from stdin
match std::io::stdout().flush() {
Ok(()) => (),
Err(_) => return None
}
let mut buffer: String = String::new();
match std::io::stdin().read_line(&mut buffer) {
Ok(_) => (),
Err(_) => return None
}
Some(buffer.trim_end().to_owned())
}
// readMaybe :: Read a => String -> Maybe a
fn read_maybe<T>(arg: &str) -> Option<T> where T: std::str::FromStr {
match arg.parse::<T>() {
Ok(res) => Some(res),
Err(_) => None
}
}
// If I can compose prompt_maybe() and read_maybe(), then I can translate the input function I have in my Haskell code.
// For reference, I *was* able to achieve something similar using the promptly crate:
use promptly::{prompt, Promptable};
fn input<T, F>(msg: &str, validator: F) -> T
where
T: Copy + std::default::Default + Promptable, F: Fn(T) -> bool,
{
loop {
let x = prompt::<T, &str>(msg).unwrap_or_default();
if validator(x) {
break x;
} else {
println!("Invalid input. Please try again.");
}
}
}
fn main() {
let s2 = prompt_maybe("What's your favourite number? ");
match s2 {
Some(s2) => println!("You entered: '{}'", s2),
None => println!("Hmm...")
}
let x2 = s2.and_then(read_maybe::<i64>); // <-- Error is thrown from this call to read_maybe()
match x2 {
Some(x2) => println!("You entered: '{}'", x2),
None => println!("Hmm...")
}
}
----------------------------------------------------------------------------
Error details:
--> src/main.rs:76:26
|
45 | fn read_maybe<T>(arg: &str) -> Option<T> where T: std::str::FromStr {
| ------------------------------------------------------------------- found signature of `for<'r> fn(&'r str) -> _`
...
76 | let x2 = s2.and_then(read_maybe::<i64>);
| ^^^^^^^^^^^^^^^^^ expected signature of `fn(std::string::String) -> _`
// I don't understand this error because I thought one could pass a String where a &str is expected.
// However, the error goes away if I change `fn read_maybe<T>(arg: &str)` to `fn read_maybe<T>(arg: String)`
// and accordingly change `match s2` to `match &s2`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment