В настоящее время я пытаюсь определить модель языка данных с тактовым потоком данных в scala.
Поток фактически представляет бесконечную последовательность значений некоторого типа T, подвергнутого воздействию некоторых часов C (часы показывают, в какие моменты поток действительно доступен).
Сэмплированный поток SF может быть выведен из потока F путем его выборки в соответствии с самой синхронизацией C, полученной из другого (булева) потока F ': SF содержит значения F, отобранные, когда логический поток F' равен true.
"Базовые часы" - это часы, полученные из всегда истинного потока с именем "T".
В приведенном ниже примере F и F 'находятся на базовых часах (и - используется, чтобы показать, что поток не имеет значения в какой-то момент)
T : 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ... (always 1)
F : 0 0 0 1 1 1 0 1 0 1 0 0 0 1 1 1 1 ...
F' : 0 1 0 0 0 1 0 1 1 1 0 0 0 1 0 0 1 ...
F sampled on F': - 0 - - - 1 - 1 0 1 - - - 1 - - 1 ...
поэтому (F, отснятый на F ') принимает значение F, когда F' истинно, и не определено, когда F 'ложно.
Цель состоит в том, чтобы сделать это отношение выборки очевидным в типе потоков и выполнить статические проверки. Например, разрешите только пробовать поток из другого, если они находятся на одних и тех же часах. (Это для DSL для моделирования цифровых схем).
Система, о которой идет речь, представляет собой зависимую систему, потому что часы являются частью типа потока и сами производятся из значения потока.
Итак, я попытался моделировать это в scala, используя типы, зависящие от пути, и вдохновения из бесформенного. Часы моделируются как типы следующим образом:
trait Clock {
// the subclock of this clock
type SubClock <: Clock
}
trait BaseClock extends Clock {
type SubClock = Nothing
}
Это определяет тип тактового сигнала и определенный тактовый сигнал, базовые часы, которые не имеют субблока.
Затем я попытался моделировать потоки:
trait Flow {
// data type of the flow (only boolean for now)
type DataType = Boolean
// clock type of the flow
type ClockType <: Clock
// clock type derived from the Flow
class AsClock extends Clock {
// Subclock is inherited from the flow type clocktype.
type SubClock = ClockType
}
}
Я определил внутренний класс в характеристике потока, чтобы иметь возможность поднять поток на часы с использованием зависимых от пути типов. если f - поток, f.AsClock - тип Clock, который может использоваться для определения выборочных потоков.
Затем я предоставляю способы создания потоков на базовых часах:
// used to restrict data types on which flows can be created
trait DataTypeOk[T] {
type DataType = T
}
// a flow on base clock
trait BFlow[T] extends Flow { type DataType = T; type ClockType = BaseClock }
// Boolean is Ok for DataType
implicit object BooleanOk extends DataTypeOk[Boolean]
// generates a flow on the base clock over type T
def bFlow[T](implicit ev:DataTypeOk[T]) = new BFlow[T] { }
Пока все хорошо. Затем я предоставляю ват для создания выборочного потока:
// a flow on a sampled clock
trait SFlow[T, C <: Clock] extends Flow { type DataType = T; type ClockType = C }
// generates a sampled flow by sampling f1 on the clock derived from f2 (f1 and f2 must be on the same clock, and we want to check this at type level.
def sFlow[F1 <: Flow, F2 <: Flow](f1: F1, f2: F2)(implicit ev: SameClock[F1, F2]) = new SFlow[f1.DataType, f2.AsClock] {}
Здесь значения потоков поднимаются до типов с использованием f2.AsClock.
Идея этого заключалась в том, чтобы писать такие вещи:
val a1 = bFlow[Boolean]
val a2 = bFlow[Boolean]
val b = bFlow[Boolean]
val c1: SFlow[Boolean, b.AsClock] = sFlow(a1, b) // compiles
val c2: SFlow[Boolean, b.AsClock] = sFlow(a2, b)
val d: SFlow[Boolean, c1.AsClock] = sFlow(a1, c1) // does not compile
и компилятор отклоняет последний случай, потому что ClockType из a1 и c1 не равен (a1 находится на базовых часах, c1 находится на часах b, поэтому эти потоки не находятся на одних и тех же часах).
Итак, я ввел (неявный ev: SameClock [F1, F2]) аргумент моему методу builder, где
SameClock - это признак, который должен засвидетельствовать во время компиляции, что два потока имеют один и тот же ClockType, и что правильно выбрать первый, используя часы, полученные из второго.
//type which witnesses that two flow types F1 and F2 have the same clock types.
trait SameClock[F1 <: Flow, F2 <: Flow] {
}
implicit def flowsSameClocks[F1 <: Flow, F2 <: Flow] = ???
Вот где я совершенно не знаю, как действовать. Я посмотрел исходный код Nat и HList в бесформенном виде и понял, что объекты, свидетельствующие об этих фактах, должны быть построены в структурном форвардном индуктивном режиме: вы предоставляете неявные сборщики для объектов, запускающих конструктор этого типа для типов, которые вы хотите статически проверять и механизм неявного разрешения генерирует объект, свидетельствующий об этом, если это возможно.
Однако я действительно не понимаю, как компилятор может построить правильный объект, используя форвардную индукцию для любого экземпляра типа, поскольку мы обычно делаем доказательства с использованием рекурсии, деконструируя термин в более простых терминах и доказывая более простые случаи.
Некоторые рекомендации от кого-то с хорошим пониманием программирования на уровне шрифтов будут полезны!