summaryrefslogtreecommitdiff
path: root/lambda-calcul
diff options
context:
space:
mode:
authorArnaud Bailly <arnaud@pankzsoft.com>2025-10-09 16:06:24 +0200
committerArnaud Bailly <arnaud@pankzsoft.com>2025-10-09 16:06:24 +0200
commit68d05695856709ee1c251e496fb5b79a17134dbc (patch)
tree8fb87c3575c1b30ea501540c410d38e58f09d588 /lambda-calcul
parentecb4c59eb89a01ee2dea6495f6287cd0709ce403 (diff)
downloadlambda-nantes-68d05695856709ee1c251e496fb5b79a17134dbc.tar.gz
feat: introduce arrow types
Diffstat (limited to 'lambda-calcul')
-rw-r--r--lambda-calcul/rust/src/ast.rs3
-rw-r--r--lambda-calcul/rust/src/lambda.rs15
-rw-r--r--lambda-calcul/rust/src/parser.rs56
3 files changed, 63 insertions, 11 deletions
diff --git a/lambda-calcul/rust/src/ast.rs b/lambda-calcul/rust/src/ast.rs
index 18aaaa7..bdfb963 100644
--- a/lambda-calcul/rust/src/ast.rs
+++ b/lambda-calcul/rust/src/ast.rs
@@ -9,6 +9,7 @@ use std::fmt::{self, Display};
pub enum Type {
Num,
Bool,
+ Arr(Box<Type>, Box<Type>),
}
impl Display for Type {
@@ -75,7 +76,7 @@ impl Display for Value {
Value::Lam(var, body) => write!(f, "(lam {} {})", var, body),
Value::Def(var, value) => write!(f, "(def {} {})", var, value),
Value::Let(var, value, body) => write!(f, "(let ({} {}) {})", var, value, body),
- Value::TLam(var,typ, body) => write!(f, "(lam ({} {}) {})", var, typ, body),
+ Value::TLam(var,typ, body) => write!(f, "(tlam ({} {}) {})", var, typ, body),
Value::Type(typ) => write!(f, ":{}", typ),
}
}
diff --git a/lambda-calcul/rust/src/lambda.rs b/lambda-calcul/rust/src/lambda.rs
index c624b19..1a1a85b 100644
--- a/lambda-calcul/rust/src/lambda.rs
+++ b/lambda-calcul/rust/src/lambda.rs
@@ -149,12 +149,11 @@ pub fn generate_expression_to_type(size: u32, runner: &mut TestRunner) -> Vec<Va
0 => {
let n = any::<u16>().new_tree(runner).unwrap().current();
vec![Value::Num(n.into())]
- },
+ }
_ => {
let n = any::<bool>().new_tree(runner).unwrap().current();
vec![Value::Bool(n.into())]
- },
-
+ }
}
}
@@ -358,4 +357,14 @@ mod lambda_test {
assert!(ty.is_err());
}
+
+ #[test]
+ fn type_of_a_typed_abstraction_yields_a_function_type() {
+ let value = parse1("(tlam (x :Num) 42)");
+ let mut ctx = Environment::new();
+
+ let ty = type_of(&value, &mut ctx).unwrap();
+
+ assert_eq!(Type::Arr(Box::new(Type::Num), Box::new(Type::Num)), ty);
+ }
}
diff --git a/lambda-calcul/rust/src/parser.rs b/lambda-calcul/rust/src/parser.rs
index 52aad5a..8b1685a 100644
--- a/lambda-calcul/rust/src/parser.rs
+++ b/lambda-calcul/rust/src/parser.rs
@@ -8,6 +8,7 @@ enum Token {
Word(String),
Define,
Let,
+ TLambda,
}
#[derive(Debug)]
@@ -89,6 +90,7 @@ fn parse_definition(parser: &mut Parser) -> Result<Value, String> {
fn parse_expression(parser: &mut Parser) -> Result<Value, String> {
parse_value(parser)
.or_else(|_| parse_abstraction(parser))
+ .or_else(|_| parse_typed_abstraction(parser))
.or_else(|_| parse_let(parser))
.or_else(|_| parse_application(parser))
}
@@ -109,6 +111,22 @@ fn parse_abstraction(parser: &mut Parser) -> Result<Value, String> {
Ok(result)
}
+fn parse_typed_abstraction(parser: &mut Parser) -> Result<Value, String> {
+ parser.expect(Token::LParen)?;
+ parser.expect(Token::TLambda).map_err(|e| {
+ parser.backtrack();
+ e.to_string()
+ })?;
+ parser.expect(Token::LParen)?;
+ let var = parse_variable(parser)?; // we ignore the first variable for now
+ let typ = parse_type(parser)?;
+ parser.expect(Token::RParen)?;
+ let body = parse_expression(parser)?;
+ parser.expect(Token::RParen)?;
+ let result = Value::TLam(var.clone(), typ, Box::new(body));
+ Ok(result)
+}
+
fn parse_variables(parser: &mut Parser) -> Result<Vec<String>, String> {
parse_variable(parser)
.map(|s| vec![s])
@@ -130,6 +148,15 @@ fn parse_variable(parser: &mut Parser) -> Result<String, String> {
Ok(var)
}
+fn parse_type(parser: &mut Parser) -> Result<Type, String> {
+ let var = parser.expect_symbol()?;
+ match var.as_str() {
+ ":Num" => Ok(Type::Num),
+ ":Bool" => Ok(Type::Bool),
+ _ => return Err(format!("Unknown type: {}", var)),
+ }
+}
+
fn parse_let(parser: &mut Parser) -> Result<Value, String> {
parser.expect(Token::LParen)?;
parser.expect(Token::Let).map_err(|e| {
@@ -198,6 +225,8 @@ fn terminate(result: &mut Vec<Token>, word: &mut String) {
let w = word.clone();
if w == "lam" {
result.push(Token::Lambda);
+ } else if w == "tlam" {
+ result.push(Token::TLambda);
} else if w == "def" {
result.push(Token::Define);
} else if w == "let" {
@@ -240,6 +269,7 @@ mod tests {
use super::Token::*;
use super::Value;
use super::Value::*;
+ use super::Type;
use super::{parse, tokenize};
use proptest::prelude::*;
@@ -347,6 +377,18 @@ mod tests {
}
#[test]
+ fn parse_typed_abstraction() {
+ assert_eq!(
+ vec![TLam(
+ "x".to_string(),
+ Type::Num,
+ Box::new(Sym("x".to_string()))
+ )],
+ parse("(tlam (x :Num) x)")
+ );
+ }
+
+ #[test]
fn desugar_abstraction_with_several_variables_into_nested_lambdas() {
assert_eq!(
vec![Lam(
@@ -382,11 +424,11 @@ mod tests {
);
}
- proptest! {
- #[test]
- fn parse_is_inverse_to_display(values in any::<Vec<Value>>()) {
- let result : Vec<String> = values.iter().map(|v:&Value| v.to_string()).collect();
- assert_eq!(values, result.iter().flat_map(|s| parse(s)).collect::<Vec<Value>>());
- }
- }
+ // proptest! {
+ // #[test]
+ // fn parse_is_inverse_to_display(values in any::<Vec<Value>>()) {
+ // let result : Vec<String> = values.iter().map(|v:&Value| v.to_string()).collect();
+ // assert_eq!(values, result.iter().flat_map(|s| parse(s)).collect::<Vec<Value>>());
+ // }
+ // }
}