Ваш пример отлично работает в разрабатываемой версии Agda. Если вы используете версию старше 2.3.2, этот отрывок из примечаний к выпуску может прояснить, почему она не компилируется для вас:
* Instance arguments resolution will now consider candidates which
still expect hidden arguments. For example:
record Eq (A : Set) : Set where
field eq : A → A → Bool
open Eq {{...}}
eqFin : {n : ℕ} → Eq (Fin n)
eqFin = record { eq = primEqFin }
testFin : Bool
testFin = eq fin1 fin2
The type-checker will now resolve the instance argument of the eq
function to eqFin {_}. This is only done for hidden arguments, not
instance arguments, so that the instance search stays non-recursive.
(исходный код)
То есть до 2.3.2 поиск экземпляра полностью игнорировал ваш экземпляр two
, потому что у него есть скрытый аргумент.
Хотя аргументы экземпляра ведут себя немного как классы типов, обратите внимание, что они будут фиксироваться в экземпляре только в том случае, если в области видимости есть только одна правильная версия типа, и они не будут выполнять рекурсивный поиск:
Instance argument resolution is not recursive. As an example,
consider the following "parametrised instance":
eq-List : {A : Set} → Eq A → Eq (List A)
eq-List {A} eq = record { equal = eq-List-A }
where
eq-List-A : List A → List A → Bool
eq-List-A [] [] = true
eq-List-A (a ∷ as) (b ∷ bs) = equal a b ∧ eq-List-A as bs
eq-List-A _ _ = false
Assume that the only Eq instances in scope are eq-List and eq-ℕ.
Then the following code does not type-check:
test = equal (1 ∷ 2 ∷ []) (3 ∷ 4 ∷ [])
However, we can make the code work by constructing a suitable
instance manually:
test′ = equal (1 ∷ 2 ∷ []) (3 ∷ 4 ∷ [])
where eq-List-ℕ = eq-List eq-ℕ
By restricting the "instance search" to be non-recursive we avoid
introducing a new, compile-time-only evaluation model to Agda.
(исходный код)
Теперь, что касается второй части вопроса: я не совсем уверен, какова ваша конечная цель, структура кода в конечном итоге зависит от того, что вы хотите сделать после создания числа. При этом я написал небольшую программу, которая позволяет вам вводить римские цифры, не просматривая явный тип данных (простите меня, если я не уловил вашего намерения ясно):
Римская цифра будет функцией, которая принимает пару натуральных чисел - значение предыдущей цифры и промежуточную сумму. Если оно меньше предыдущего числа, мы вычтем его значение из промежуточной суммы, в противном случае мы добавим его. Возвращаем новый нарастающий итог и значение текущего числа.
Конечно, это далеко не идеально, потому что ничто не мешает нам набрать I I X
, и в итоге мы оцениваем его как 10. Я оставляю это как упражнение для заинтересованного читателя. :)
Сначала импорт (обратите внимание, что здесь я использую стандартную библиотеку, если вы не хотите ее устанавливать, вы можете просто скопировать определение из онлайн-репо):
open import Data.Bool
open import Data.Nat
open import Data.Product
open import Relation.Binary
open import Relation.Nullary.Decidable
Это наша цифровая фабрика:
_<?_ : Decidable _<_
m <? n = suc m ≤? n
makeNumeral : ℕ → ℕ × ℕ → ℕ × ℕ
makeNumeral n (p , c) with ⌊ n <? p ⌋
... | true = n , c ∸ n
... | false = n , c + n
И мы можем составить несколько цифр:
infix 500 I_ V_ X_
I_ = makeNumeral 1
V_ = makeNumeral 5
X_ = makeNumeral 10
Затем мы должны применить эту цепочку функций к чему-либо, а затем извлечь промежуточную сумму. Это не самое лучшее решение, но в коде оно выглядит красиво:
⟧ : ℕ × ℕ
⟧ = 0 , 0
infix 400 ⟦_
⟦_ : ℕ × ℕ → ℕ
⟦ (_ , c) = c
И наконец:
test₁ : ℕ
test₁ = ⟦ X I X ⟧
test₂ : ℕ
test₂ = ⟦ X I V ⟧
Оценка test₁
через C-c C-n
дает нам 19
, test₂
, затем 14
.
Конечно, вы можете переместить эти инварианты в тип данных, добавить новые инварианты и так далее.
person
Vitus
schedule
18.08.2013