Как я могу превратить [TExp a] в TExp [a] или каким-либо другим образом применить fineTH к нескольким значениям программно? - PullRequest
3 голосов
/ 11 марта 2020

Я недавно использовал уточненный для уточнения типов в Haskell и столкнулся с серьезной проблемой юзабилити. Я не могу понять, как уточнить весь список значений во время компиляции.

Например, я могу написать:

{-# LANGUAGE TemplateHaskell #-}

import Refined

oneToThree :: [Refined Positive Int]
oneToThree = [$$(refineTH 1), $$(refineTH 2), $$(refineTH 3)]

Но я не могу сделать это, исключает возможность используя синтаксис диапазона, потому что Refined (по уважительной причине) не имеет экземпляра для Enum.

Я хотел бы иметь возможность сделать что-то вроде

oneToThree :: [Refined Positive Int]
oneToThree = $$(traverse refineTH [1..3])

, но Я не могу заставить это скомпилировать, потому что я не могу поднять [TExp (Refined Positive Int)] в TExp [Refined Positive Int].

Есть ли шаблон haskell magi c, который я пропустил, который позволит мне сделать это?

Также будут открыты предложения по улучшению облегченных библиотек типов уточнения, если у кого-то есть предложение.

Ответы [ 2 ]

2 голосов
/ 11 марта 2020
sequenceQTExpList :: [Q (TExp a)] -> Q (TExp [a])
sequenceQTExpList [] = [|| [] ||]
sequenceQTExpList (x:xs) = [|| $$(x) : $$(sequenceQTExpList xs) ||]

Тогда используйте это как

$$(sequenceQTExpList $ map refineTH [1..3])

Вы правы, что это похоже на траверс. Тем не менее, этот тип немного отличается, с дополнительными Q с плавающими вокруг. Я не вижу ничего лишнего, что позволяет вам с пользой комбинировать эти слои.

К сожалению, во многих механизмах используется синтаксис TH, а не функции. Просто не существует очевидного способа выполнять как подъем, так и сращивание как функции, поэтому вы застряли в написании специальных помощников для каждого типа контейнера вместо использования Traversable. Это интересная проблема. Если есть чистое решение, у него будет хороший шанс превратить его в будущую версию шаблона Haskell, если он будет представлен сопровождающим. Но я просто не вижу этого сейчас.

0 голосов
/ 11 марта 2020

Это работает (он должен находиться в файле, отличном от того, в котором вы его используете, хотя из-за ограничения стадии):

import Language.Haskell.TH.Syntax (Exp(ListE), TExp(TExp))

makeTypedTHList :: [TExp a] -> TExp [a]
makeTypedTHList xs = TExp $ ListE [x | TExp x <- xs]

Затем вы бы использовали его так:

{-# LANGUAGE TemplateHaskell #-}

import Refined
import AboveCodeInSeparateModuleBecauseOfStageRestriction (makeTypedTHList)

oneToThree :: [Refined Positive Int]
oneToThree = $$(makeTypedTHList <$> traverse refineTH [1..3])

Тем не менее, сам вызов TExp конструктора подрывает некоторую безопасность напечатанного Template Haskell (хотя я думаю, что этот конкретный случай безопасен). В идеале я бы предпочел подход, который не требовал этого, но я не могу придумать один.

...