Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add failing tests #29

Merged
merged 5 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions scall-core/src/main/scala/io/chymyst/dhall/TypeCheck.scala
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@
typeError(
s"Expression `assert` failed: Unequal sides, ${lop.alphaNormalized.betaNormalized.print} does not equal ${rop.alphaNormalized.betaNormalized.print}, in ${exprN.print}"
)
case other => typeError(s"An `assert` expression must have an equality type but has ${other.print}")
case other => typeError(s"An `assert` expression must have an equality type but has type ${other.print}")
}
case errors => errors
}
Expand All @@ -630,11 +630,27 @@
}

case tt @ Expression(Application(Expression(ExprBuiltin(Builtin.Optional)), t)) =>
if (pathComponents.head.isOptionalLabel) {
validate(gamma, body, t).map(_ => tt)
} else typeError(s"An Optional value must be updated with `?` but instead found ${pathComponents.head}")
pathComponents.head match {
case PathComponent.Label(fieldName) =>
typeError(s"An Optional value must be updated with `?` but instead found ${fieldName}")

Check warning on line 635 in scall-core/src/main/scala/io/chymyst/dhall/TypeCheck.scala

View check run for this annotation

Codecov / codecov/patch

scall-core/src/main/scala/io/chymyst/dhall/TypeCheck.scala#L635

Added line #L635 was not covered by tests
case PathComponent.DescendOptional =>
pathComponents.tail match {
case Seq() => // The expression is (Some x) with ? = body. The type of this expression is Optional Y where body : Y.
// We use `validate` to impose the constraint that body's type is `t`. See https://github.com/dhall-lang/dhall-haskell/issues/2597
validate(gamma, body, t).map(_ => tt)
case tail => // The expression is (Some x) with ?.a.b = body. The type of this expression is Optional Y where Y is the type of `x with a.b = body`. Here, `x` is a new variable that should not be free in `body`.
val newVar = VarName("check_type_of_with")
val newBody = Semantics.shift(positive = true, newVar, BigInt(0), body)
val newWith = With(Expression(Variable(newVar, BigInt(0))), tail, newBody)
val newGamma = gamma.prependAndShift(newVar, t)
// newWith.inferTypeWith(newGamma).map(t1 => Expression(Application(Expression(ExprBuiltin(Builtin.Optional)), t1))) // This would allow changing types after `with`.
validate(newGamma, newWith, t).map(_ => tt)

case other => typeError(s"A `with` expression's arg must have record type, but instead found ${other.print}")
}

}

case other => typeError(s"A `with` expression's argument must have record type or Optional type, but instead found type ${other.print}")
}

case DoubleLiteral(_) => Builtin.Double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,6 @@ class SimpleSemanticsTest extends DhallTest {

test("failure in eta expansion with two curried arguments") {
val failure = "λ(f : Bool → Bool → Bool) → assert : f === (λ(x : Bool) → λ(y : Bool) → f y x)".dhall.inferTypeWith(KnownVars.empty)
println(failure)
expect(failure match {
case TypecheckResult.Invalid(errors) => errors exists (_ contains "Unequal sides, f does not equal λ(_ : Bool) → λ(_ : Bool) → f _ _@1")
})
Expand All @@ -383,15 +382,13 @@ class SimpleSemanticsTest extends DhallTest {

test("failure 1 with f x in eta expansion with free occurrences of external bound variable") {
val failure = "λ(f : Bool → Bool → Bool) → λ(x : Bool) → assert : f x === (λ(x : Bool) → f x x)".dhall.inferTypeWith(KnownVars.empty)
println(failure)
expect(failure match {
case TypecheckResult.Invalid(errors) => errors exists (_ contains "Unequal sides, f x does not equal λ(_ : Bool) → f _ _, in f x ≡ (λ(x : Bool) → f x x)")
})
}

test("failure 2 with f x in eta expansion with free occurrences of external bound variable") {
val failure = "λ(f : Bool → Bool → Bool) → λ(x : Bool) → assert : f === (λ(x : Bool) → f x x)".dhall.inferTypeWith(KnownVars.empty)
println(failure)
expect(failure match {
case TypecheckResult.Invalid(errors) =>
errors exists (_ contains "Types of two sides of `===` are not equivalent: ∀(_ : Bool) → ∀(_ : Bool) → Bool and ∀(x : Bool) → Bool")
Expand Down Expand Up @@ -512,4 +509,29 @@ class SimpleSemanticsTest extends DhallTest {
expect(!Semantics.equivalent(x, y))
}

test("`with` for Optional works if it does not change type") { // https://github.com/dhall-lang/dhall-haskell/issues/2597
val result = "(Some 1) with ? = 2".dhall.typeCheckAndBetaNormalize()
expect(result.unsafeGet.print == "Some 2")
}

test("fail `with` for Optional if it changes type") {
val result = """(Some 1) with ? = "hello"""".dhall.typeCheckAndBetaNormalize()
expect(
Try(
result.unsafeGet
).failed.get.getMessage contains "Inferred type Text differs from the expected type Natural, expression under type inference: \"hello\""
)
}

test("`with` for Optional works if it does not change type, with deep record access") { // https://github.com/dhall-lang/dhall-haskell/issues/2597
val result = "(Some { x.y = 1 }) with ?.x.y = 2".dhall.typeCheckAndBetaNormalize()
expect(result.unsafeGet.print == "Some { x = { y = 2 } }")
}

test("fail `with` for Optional if it changes type, with deep record access") {
val result = """(Some { x.y = 1 }) with ?.x.y = "hello"""".dhall.typeCheckAndBetaNormalize()
// expect(result.unsafeGet.print == "Some { x = { y = \"hello\" } }")
expect(Try(result.unsafeGet).failed.get.getMessage contains "Inferred type { x : { y : Text } } differs from the expected type { x : { y : Natural } }")
}

}
4 changes: 4 additions & 0 deletions tutorial/programming_dhall.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ Identifiers may be arbitrary characters (even keywords or whitespace) if escaped
3
```

The standalone underscore character `_` in Haskell and Scala is a syntax for a special "unused" variable.
But in Dhall, the variable named `_` is a variable like any other.

### Primitive types

Integers must have a sign (`+1` or `-1`) while `Natural` numbers may not have a sign (`123`).
Expand Down Expand Up @@ -7027,6 +7030,7 @@ p G unfixf (fmap_K T G (unfold T cT) kt)
as long as the precondition of the law holds:

`fmap_F T G (unfold T cT) (cT x) === unfixf (unfold T cT x)`

This equation (after setting `R = T` and `rfr = cT`) was derived in Statement 2 in the section "Properties of co-inductive types".

This allows us to complete the proof of item 2:
Expand Down
Loading