Fakta benar yang bagus tentang penggabungan adalah bahwa jika saya tahu ada dua variabel dalam persamaan:
a ++ b = c
Lalu aku tahu yang ketiga.
Saya ingin menangkap ide ini di konser saya sendiri jadi saya menggunakan dependensi fungsional.
{-# Language DataKinds, GADTs, FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, PolyKinds, TypeOperators, UndecidableInstances #-}
import Data.Kind (Type)
class Concatable
(m :: k -> Type)
(as :: k)
(bs :: k)
(cs :: k)
| as bs -> cs
, as cs -> bs
, bs cs -> as
where
concat' :: m as -> m bs -> m cs
Sekarang saya membuat daftar heterogen seperti:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
Tetapi ketika saya mencoba untuk menyatakan ini karena Concatable
saya memiliki masalah
instance Concatable HList '[] bs bs where
concat' HEmpty bs = bs
instance
( Concatable HList as bs cs
)
=> Concatable HList (a ': as) bs (a ': cs)
where
concat' (HCons head tail) bs = HCons head (concat' tail bs)
Saya tidak memenuhi ketergantungan fungsional ketiga saya. Atau lebih tepatnya kompiler percaya kita tidak. Ini karena kompiler percaya bahwa pada instance kedua kita mungkin demikian bs ~ (a ': cs)
. Dan itu bisa terjadi jika Concatable as (a ': cs) cs
.
Bagaimana saya bisa menyesuaikan instance saya sehingga ketiga dependensi puas?
haskell
typeclass
functional-dependencies
type-level-computation
Sriotchilism O'Zaic
sumber
sumber
bs cs -> as
, karena kita memerlukan informasi non-lokal tentangbs
dancs
untuk memutuskan apakahas
harus kontra atau nol. Kita perlu mengetahui bagaimana cara merepresentasikan informasi ini; konteks apa yang akan kita tambahkan ke tanda tangan jenis untuk menjaminnya ketika tidak dapat disimpulkan secara langsung?bs
dancs
, dan kita ingin mengeksploitasi fundep, yaitu kita ingin merekonstruksias
. Untuk melakukannya dengan cara yang deterministik, kami berharap dapat berkomitmen pada satu instance dan mengikuti resep itu. Konkret, asumsikanbs = (Int ': bs2)
dancs = (Int ': cs2)
. Contoh mana yang kita pilih? Ada kemungkinan bahwaInt
in tersebutcs
berasal daribs
(danas
kosong). Mungkin juga yang berasal dari (as
bukan kosong) sebagai gantinya, dan ituInt
akan muncul lagics
nanti. Kita perlu menggali lebih dalamcs
untuk mengetahui dan GHC tidak akan melakukan itu.Jawaban:
Jadi, seperti komentar yang disarankan, GHC tidak akan mengetahuinya sendiri, tetapi kami dapat membantu dengan sedikit pemrograman tingkat tipe. Mari kita perkenalkan beberapa
TypeFamilies
. Semua fungsi ini adalah terjemahan yang cukup mudah dari manipulasi daftar yang diangkat ke tingkat tipe:Dengan alat ini yang kami miliki, kami benar-benar dapat mencapai sasaran jam, tetapi pertama-tama mari kita mendefinisikan fungsi dengan properti yang diinginkan:
cs
darias
danbs
as
daribs
dancs
bs
darias
dancs
Voila:
Mari kita mengujinya:
Dan akhirnya tujuan akhir:
sumber