Соответствие C StablePtr a
является typedef для void *
- защита от потери на границе FFI.
Проблема в том, что существует бесконечное число возможностей для a :: *
, поэтому для StablePtr a
. Кодирование этих типов в C, которое имеет систему ограниченного типа (без параметрических типов!), Не может быть выполнено, если не прибегать к очень унииоматическим типам C (см. Ниже).
В вашем конкретном случае, a, b :: Sz
, поэтому у нас есть только конечное число случаев, и некоторый инструмент FFI может помочь в кодировании этих случаев. Тем не менее, это может привести к взрыву комбинаторного случаев:
typedef struct HsStablePtr_Selection_ { void *p; } HsStablePtr_Selection;
typedef struct HsStablePtr_Reduction_ { void *p; } HsStablePtr_Reduction;
HsStablePtr_Selection
add_Selection_Selection(HsStablePtr_Selection a, HsStablePtr_Selection b);
HsStablePtr_Selection
add_Selection_Reduction(HsStablePtr_Selection a, HsStablePtr_Reduction b);
HsStablePtr_Selection
add_Reduction_Selection(HsStablePtr_Reduction a, HsStablePtr_Selection b);
HsStablePtr_Reduction
add_Reduction_Reduction(HsStablePtr_Reduction a, HsStablePtr_Reduction b);
В С11 можно уменьшить этот беспорядок, используя type-generic expressions, , который может добавить типа «вправо» отбрасывает без комбинаторных взрыва. Тем не менее, никто не написал инструмент FFI, использующий это. Например:
void *add_void(void *x, void *y);
#define add(x,y) \
_Generic((x) , \
HsStablePtr_Selection: _Generic((y) , \
HsStablePtr_Selection: (HsStablePtr_Selection) add_void(x,y), \
HsStablePtr_Reduction: (HsStablePtr_Selection) add_void(x,y) \
) \
HsStablePtr_Reduction: _Generic((y) , \
HsStablePtr_Selection: (HsStablePtr_Selection) add_void(x,y), \
HsStablePtr_Reduction: (HsStablePtr_Reduction) add_void(x,y) \
) \
)
(. Брикеты выше взяты из указателя на структуру, так что они не работают, и мы должны использовать STRUCT литералов вместо этого, но давайте игнорировать это)
В C++ мы бы богаче типы для использования, но FFI предназначен для использования C как общего lingua franca для привязки к другим языкам. (! Мономорфная)
Возможное кодирование Haskell параметрические типа может быть достигнуто, теоретически, эксплуатировать только конструкторы типа С имеет: указатели, массивы, указатели на функции, константные, летучие, ....
например, стабильный указатель на type T = Either Char (Int, Bool)
может быть представлена следующим образом:
typedef struct HsBool_ { void *p } HsBool;
typedef struct HsInt_ { void *p } HsInt;
typedef struct HsChar_ { void *p } HsChar;
typedef struct HsEither_ HsEither; // incomplete type
typedef struct HsPair_ HsPair; // incomplete type
typedef void (**T)(HsEither x1, HsChar x2
void (**)(HsPair x3, HsInt x4, HsBool x5));
конечно, с точки зрения с, типом T
является грубой ложью !! значение типа T
на самом деле было бы void *
, указывающее на некоторое представление на стороне Haskell типа StablePtr T
и, конечно же, не указатель на указатель на функцию C! Тем не менее, прохождение T
вокруг сохранит безопасность типов.
Обратите внимание, что вышеупомянутое можно назвать только «кодированием» в очень слабом смысле, а именно, это инъективное отображение из мономорфных типов Haskell в типы C, полностью игнорируя семантику типов C. Это делается только для того, чтобы, если такие стабильные указатели были возвращены в Haskell, на стороне C есть проверка определенного типа.
Я использовал C неполных типов, так что вы никогда не можете вызвать эти функции в C. Я использовал указатели для указателей, поскольку указатели (IIRC) на функции не могут быть переведены в void *
безопасно.
Обратите внимание, что такая сложная кодировка может использоваться в C, но может быть трудно интегрировать с другими языками. Например, Java и Haskell могут взаимодействовать с JNI + FFI, но я не уверен, что часть JNI может справиться с такой сложной кодировкой. Возможно, void *
более практичен, хотя и небезопасен.
Безопасно кодирующая полиморфные функции, GADTs, классы типа ... остается для будущей работы :-P
TL; DR: ИПП может стараться кодировать статические типы в C, но это сложно, и на данный момент нет большого спроса на это. Возможно, в будущем это может измениться.
C, вероятно, не имеет достаточно прочной системы для проверки любой части вашего DSL. Может быть, C++ шаблоны могли бы это сделать .. но это кошмар. Я думаю, вы должны признать, что функция * может * быть вызвана неправильно на стороне C и проверить на стороне Haskell, что это было во время выполнения. Конечно, вы все равно можете получить информацию о типе во время выполнения (например, с помощью «Тип»), чтобы вы все еще могли использовать ваши хорошо типизированные функции Haskell (после того, как вы правильно набрали ввод из C). – user2407038