Pertanyaan Menangani jenis yang ambigu dalam DSLs bebas yang dapat dikompilasi


Saya sedang membangun beberapa DSL yang seharusnya bisa dibuat berdasarkan 'monads gratis' dan 'tipe data a la carte', menggunakan bebas dan compdata paket (serupa dalam semangat untuk Menggabungkan jenis-jenis Gratis).

Sementara ini bekerja untuk beberapa DSL sederhana, saya terjebak pada yang memiliki parameter tipe, dalam kasus konstruktor / perintah yang tidak bergantung pada parameter jenis ini, menyebabkan parameter jenis rancu kesalahan dari GHC.

Untuk memperjelas, inilah beberapa kode:

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}

module DSL where

import Data.Comp
import Control.Monad.Free

type Index = Int

data DSL a next = Read Index (a -> next)
                | Write a (Index -> next)
                | GetLastIndex (Index -> next)
    deriving (Functor)

read :: (Functor f, DSL a :<: f, MonadFree f m) => Index -> m a
read idx = liftF (inj (Read idx id))

write :: (Functor f, DSL a :<: f, MonadFree f m) => a -> m Index
write a = liftF (inj (Write a id))

-- This works
getLastIndex' :: MonadFree (DSL a) m => m Index
getLastIndex' = liftF (GetLastIndex id)

-- This doesn't:
--
--     Could not deduce (Data.Comp.Ops.Subsume
--                         (compdata-0.10:Data.Comp.SubsumeCommon.ComprEmb
--                            (Data.Comp.Ops.Elem (DSL a0) f))
--                         (DSL a0)
--                         f)
--     from the context (Functor f, DSL a :<: f, MonadFree f m)
--       bound by the type signature for
--                  getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index
--       at simple.hs:30:17-66
--     The type variable ‘a0’ is ambiguous
--     In the ambiguity check for the type signature for ‘getLastIndex’:
--       getLastIndex :: forall (m :: * -> *) (f :: * -> *) a.
--                       (Functor f, DSL a :<: f, MonadFree f m) =>
--                       m Index
--     To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
--     In the type signature for ‘getLastIndex’:
--       getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index

getLastIndex :: (Functor f, DSL a :<: f, MonadFree f m) => m Index
-- getLastIndex = liftF (inj (GetLastIndex id))
getLastIndex = _

Mencoba membuatnya berfungsi dengan mengaktifkan AllowAmbiguousTypes ekstensi seperti yang diisyaratkan oleh GHC tidak membuat saya lebih jauh. Saya mencoba menambahkan beberapa untuk semua-stik hal dalam tanda tangan jenis, tidak berhasil.

Apakah ada cara untuk membuat pola ini berfungsi?


4
2018-05-05 16:21


asal


Jawaban:


Ini adalah batasan jumlah terbuka "à la Carte" yang relatif terkenal.

Singkatnya, jika kita memiliki f functor itu sendiri memiliki satu atau lebih tipe indeks dalam, kemudian jenis inferensi menderita secara signifikan untuk jumlah terbuka yang mengandung functor tersebut.

Dengan mengilustrasikan alasannya, misalkan kita memiliki jumlah terbuka yang berisi DSL () dan a DSL Int. GHC harus memilih instance untuk salah satunya, tetapi tidak mungkin dengan getLastIndex, karena a parameter tidak disebutkan dalam argumen atau jenis kembalinya. GHC pada dasarnya tidak memiliki informasi tentang a dari konteksnya.

Ini dapat diperbaiki dengan cara yang agak kikuk Data.Proxy:

import Data.Proxy

getLastIndex ::
  forall a f m.
  (Functor f, DSL a :<: f, MonadFree f m)
  => Proxy a -> m Index
getLastIndex _ = liftF (inj (GetLastIndex id :: DSL a Index))

Sebagai alternatif, kita dapat memulihkan inferensi jenis yang bagus dan unambiguitas jika kita mengharuskan hanya ada satu DSL dalam jumlah terbuka. Namun, ini melibatkan penulisan ulang kode pencarian level-level untuk :<: untuk fungsi seperti DSL (mereka yang memiliki indeks tipe batin). Kami tidak bisa melakukan ini compdata seperti itu, karena tidak mengekspor yang relevan mesin level-level.

Saya menulis a contoh minimal untuk apa penerapan di atas terlihat seperti untuk kasus Anda. Saya tidak menempelkannya di sini karena agak panjang dan tidak merata. Perhatikan bahwa pilihan indeks bagian dalam menjadi sepenuhnya ditentukan oleh konstruktor functor dan jumlah terbuka. Ini juga memperbaiki jenis inferensi untuk kasus lain; misalnya dengan kode lama kita harus mengetikkan-anotasi setiap penggunaan Write x f jika x adalah angka literal atau nilai polimorfik apa pun, sedangkan dalam kode baru, disimpulkan.

Juga, perhatikan bahwa penerapan contoh hanya untuk fungsi dengan indeks dalam tunggal! Jika kami ingin memiliki DSL a b next, maka kita harus menulis kode baru untuk kasus itu juga, atau gunakan DSL '(a, b) next sebagai gantinya.


3
2018-05-05 21:12