Analysieren, nicht validieren

Bereits im Dezember stieß ich auf einen absolut bemerkenswerten Artikel in englischer Sprache, der sich mit der Verwendung des Sprachtypsystems für eine breitere Aufgabenklasse befasste, um die Zuverlässigkeit von Anwendungen zu erhöhen und das Refactoring zu vereinfachen. Leider war ich in diesem Moment zu beschäftigt damit, Artikel über AF zu schreiben, was es äußerst wichtig war, zu schreiben, solange die Erinnerungen noch frisch sind. Aber jetzt, als ich mit dieser Aufgabe fertig wurde, kam ich endlich dazu, diese wundervolle Notiz zu übersetzen. Die Originalsprache der Beispiele ist Haskell, aber ich habe beschlossen, sie in einem Rast zu kopieren, um eine größere Reichweite des Publikums zu erreichen. Die Sprache hier ist jedoch völlig unwichtig. Die Tipps dieses Artikels, die ich in der täglichen Entwicklung für vollständig „banales“ C # und TypeScript verwende. Wenn Sie also nur versuchen, zuverlässigen und unterstützten Code zu schreiben, befindet sich der Artikel unabhängig von der Sprache im Betreff.


Vielen Dank für das Korrekturlesen und die Unterstützung bei der Übersetzung. Hirrolot, Funkill und andreevlex



Über einen ziemlich langen Zeitraum hatte ich Mühe, einen genauen und einfachen Weg zu finden, um zu erklären, was typgesteuertes Design ist ( im Folgenden: TypeDD, ca. Trans. ). Sehr oft fragten sie mich: "Wie sind Sie auf eine solche Entscheidung gekommen?", Aber ich hatte keine zufriedenstellende Antwort. Ich weiß mit Sicherheit, dass es mir nicht als Vision gekommen ist: Ich hatte einen iterativen Entwurfsprozess, der mich nicht dazu zwang, die „richtige“ Architektur aus dem Nichts herauszuholen, aber bisher war ich nicht in der Lage, diesen Prozess für andere zu interpretieren.


: 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>

, headNone, , 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>);

, NonEmptyVecT (, ) 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