Parse, not validate

Back in December, I came across one absolutely wonderful article in English, devoted to the use of the language type system for a wider class of tasks, to increase the reliability of applications and the simplicity of refactoring. Unfortunately, at that moment I was too busy writing articles on AF, which it was extremely important to write while the memories are still fresh. But now, when I coped with this task, I finally got around to translate this wonderful note. The original language of the examples is Haskell, but I decided to copy them to a rast, for a wider audience. However, the language here is completely unimportant, the tips of this article I use in daily development on completely β€œmundane” C # and TypeScript, so if you just try to write reliable and supported code, then, regardless of the language, the article will be in the subject.


Thank you for proofreading and translation assistance. Hirrolot, funkill and andreevlex



Over a fairly long period of time, I struggled to find an accurate, simple way to explain what Type-Driven Design is ( hereinafter - TypeDD, approx. Trans. ). Quite often they asked me β€œHow did you come up with such a decision?”, But I did not have a satisfactory answer. I know for sure that it did not come to me as a vision: I had an iterative design process that did not oblige me to get the β€œcorrect” architecture out of nothing right now, but so far I have not been very able to interpret this process for others.


: JSON , , . , , , TypeDD, , , :


, .


TypeDD


, : TypeDD, , , . , . , , , .



, , , " ?". :


enum Void {}

fn foo(x: i32) -> Void

? , β€” , .. Void β€” , , Void. , , :


fn head<T>(xs: Vec<T>) -> T

. ? , - , , :


fn head<T>(xs: Vec<T>) -> T {
    match xs.as_slice() {
        [x, ..] => *x
    }
}

error[E0004]: non-exhaustive patterns: `&[]` not covered
 --> src/lib.rs:2:11
  |
2 |     match xs.as_slice() {
  |           ^^^^^^^^^^^^^ pattern `&[]` not covered
  |

, , , . , , [], . , .. , , , ! , , , ( , . .).



- . , . , " " - , . head, , .



, head , .. : , . , : . , , , , : , , . Rust Option:


fn head<T>(xs: Vec<T>) -> Option<T>

, head β€” None, , T:


fn head<T>(xs: Vec<T>) -> Option<T> {
    match xs.as_slice() {
        [x, ..] => Some(*x),
        [] => None,
    }
}

, ? , β€” … .


Option, , , head. . head None, , . , :


fn get_configuration_directories() -> Result<Vec<String>, &'static str> {
    let config_dirs_string = std::env::var("CONFIG_DIRS").map_err(|_| "cannot read env")?;
    let list: Vec<_> = config_dirs_string.split(',').map(|x| x.to_string()).collect();
    if list.is_empty() {
        return Err("CONFIG_DIRS cannot be empty");
    }
    Ok(list)
}

fn main() -> Result<(), &'static str> {
    let config_dirs = get_configuration_directories()?;
    match head(config_dirs) {
        Some(cacheDir) => initialize_cache(cacheDir),
        None => panic!("should never happen; already checked config_dirs is non-empty")
    }
    Ok(())
}

get_configuration_directories , . head main, , Option<&str> None, , , ! :


  1. -, . , , ?


  2. -, . , , , , , .


  3. , , . , get_configuration_directories , , , ? , main, "" , .



. None , get_configuration_directories .
, , .



, head , . , - : , , head , , , . ?


() head .


fn head<T>(xs: Vec<T>) -> T

, . , , : ( β€” Vec<T>). , head .


, , .
, NonEmptyVec . :


struct NonEmptyVec<T>(T, Vec<T>);

, NonEmptyVec β€” T (, ) Vec<T>. , Vec<T> [], . , head :


fn head<T>(xs: NonEmptyVec<T>) -> T {
    xs.0
}

, , , , . :


fn get_configuration_directories() -> Result<NonEmptyVec<String>, &'static str> {
    let config_dirs_string = std::env::var("CONFIG_DIRS").map_err(|_| "cannot read env")?;
    let list: Vec<_> = config_dirs_string.split(',').map(|x| x.to_string()).collect();
    match non_empty(list) {
        Some(x) => Ok(x),
        None => Err("CONFIG_DIRS cannot be empty")
    }
}

fn main() -> Result<(), &'static str> {
    let config_dirs = get_configuration_directories()?;
    initialize_cache(head(config_dirs));
    Ok(())
}

, main ! , get_configuration_directories. NonEmptyVec Vec non_empty, :


fn non_empty<T>(list: Vec<T>) -> Option<NonEmptyVec<T>>

, Option , None : . , NonEmptyVec, ( !) , . , NonEmptyVec Vec<T> , .


head , :


  • , .


  • , get_configuration_directories , . , main , !



, head , non_empty:


fn old_head<T>(xs: Vec<T>) -> Option<T> {
    non_empty(xs).map(head)
}

, : head . , .



? , β€” , , . , : , , . :


fn validate_non_empty<T>(xs: Vec<T>) -> Result<(), UserError> {
    if !xs.is_empty() {
        Ok(())
    } else {
        Err(UserError::new("list cannot be empty"))
    }
}

fn parse_non_empty<T>(mut xs: Vec<T>) -> Result<NonEmptyVec<T>, UserError> {
    if !xs.is_empty() {
        let head = xs.remove(0);
        Ok(NonEmptyVec(head, xs))
    } else {
        Err(UserError::new("list cannot be empty"))
    }
}

: , , . : validate_non_empty (), , , parse_non_empty NonEmptyVec<T>, , . , parse_non_empty , validate_non_empty .


: validate_non_empty , parse_non_empty , . , parse_non_empty , , ", ". parse_non_empty. - , ? , , , , parse_non_empty , .


: ? , , , . , β€” β€” , - . , , , parse_non_empty : , .


: , , , ! Rust , :



: . - -, , . , , , , , .


, , : , . : - , . , NonEmpty : , .



, , , . , ? , , , ?


, . Ad-hoc , - . 2016 The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them :


β€” , , , ; , ( - ), "" .

, :


, . , - , .

, , , , , , , . β€” , , , β€” .


, β€” , , . , , , , "" . , , .


, : , , , . , .


, ,


, . ", , !", , , . "" "", "".


: .


, , , , , - . β€” , , :


fn check_no_duplicate_keys<K: Eq, V>(xs: &[(K,V)]) { ... }

"" β€” : . - , , , . , , , HashMap. , HashMap , , .


, , , , , . - , HashMap . , , , . check_no_duplicate_keys:


fn check_no_duplicate_keys<K: Eq, V>(xs: &[(K,V)]) -> HashMap<K,V> { ... }

, !


:


  1. , . , . , , .


  2. , . , . , , , .


    , . - , .



, , , , . , , - . , - !


, :


  • , . bool , ( , , . .). , β€” , , ,


  • Result<(), Error> . , - , , .


  • . , , , , , , . β€” -.


  • , , : .


    • . , , , .

  • , " " . , , , , , . (newtype) , - .



, . , error "impossible" - β€” , , . , , .



, -. , , β€” ! , .


. , β€” " " β€” . , , , . , β€” ! β€” . , β€” .


, , : Type Safety Back and Forth. , . Ghosts of Departed Proofs, , , .


, , , . , β€” , . , TypeDD, , , , . , , . , β€” .





, β€” , .


All Articles