2017-01-24 14 views
1

ghc 8で従属型を使用していますが、私の型のShowインスタンスを作成する際に問題が発生しています。従属型のインスタンスを表示する

#!/usr/bin/env stack 
-- stack exec --resolver=lts-7.14 --package singletons -- ghci 
{-# LANGUAGE GADTs, ScopedTypeVariables, TypeInType, TemplateHaskell, LambdaCase, TypeApplications #-} 

import Data.Kind 
import Data.Singletons.Prelude 
import Data.Singletons.TypeLits 

data EmailAddress :: Symbol -> Symbol -> * where 
    EmailAddress :: (KnownSymbol a, KnownSymbol b) => EmailAddress a b 

-- works 
testEmail :: EmailAddress "blah" "blah.com" 
testEmail = EmailAddress 

私は自分のアドレスを表示したいと思っていました。

instance Show (EmailAddress a b) 
    where show = showEmailAddress 

showEmailAddress :: forall a b. EmailAddress a b -> String 
showEmailAddress = \case 
    EmailAddress -> symbolVal (Proxy :: Proxy a) ++ "@" ++ symbolVal (Proxy :: Proxy b) 

test1 = show testEmail -- works 

ランタイムEmailAddressをユーザー提供の文字列から作成します。まず、シングルトンからアドレスを取得します。ちょうど今のところ1本の弦を取る

fromString' :: Sing i -> EmailAddress i i 
fromString' = \case 
    SSym -> EmailAddress 

test2 = fromString' @"asdf" sing -- works 

パズルの最後の部分は、次のようなことができるはずです。

fromString :: String -> EmailAddress a a 
fromString str = case toSing str of 
    SomeSing s -> fromString' s 

しかし、動作しません。関数のさまざまな部分にどのような型のシグネチャを適用しても、型チェックを行うことができません。次のコードでは、常に次のエラーが発生します。Couldn't match type ‘a’ with ‘a1’エラー。

fromString1 :: String -> EmailAddress a a 
fromString1 str = case toSing str of 
    SomeSing s -> fromString' s 

fromString2 :: forall a. String -> EmailAddress a a 
fromString2 str = case toSing str of 
    SomeSing (s :: Sing a) -> fromString' s 

fromString3 :: forall a. String -> EmailAddress a a 
fromString3 str = case toSing str of 
    (SomeSing s :: Sing (a :: Symbol)) -> fromString' s 

これは完全なエラーです。私はa1がどこから来ているのか分かりません。

Existentials5.hs:45:20: error: 
• Couldn't match type ‘a’ with ‘a1’ 
    ‘a’ is a rigid type variable bound by 
    the type signature for: 
     fromString3 :: forall (a :: Symbol). String -> EmailAddress a a 
    at Existentials5.hs:43:16 
    ‘a1’ is a rigid type variable bound by 
    a pattern with constructor: 
     SomeSing :: forall k k1 (k2 :: k1) (a :: k). Sing a -> SomeSing k, 
    in a case alternative 
    at Existentials5.hs:45:5 
    Expected type: EmailAddress a a 
    Actual type: EmailAddress a1 a1 
• In the expression: fromString' s 
    In a case alternative: (SomeSing s) -> fromString' s 
    In the expression: 
    case toSing str of { (SomeSing s) -> fromString' s } 
• Relevant bindings include 
    s :: Sing a1 (bound at Existentials5.hs:45:14) 
    fromString3 :: String -> EmailAddress a a 
     (bound at Existentials5.hs:44:1) 

答えて

3

fromString :: String -> EmailAddress a aは不可能です。 fromString @a ""aの場合はKnownSymbol aとなりますが、GHCはSymbol -sを含むすべての種類のプログラムを消去するため、このようなことは起こりません。 aに対応するランタイムStringを呼び出すことはできません。これが最初にシングルトンを使用する必要がある理由です。

別の観点から、String -> EmailAddress a aの問題は、我々は、特定のaためKnownSymbol a出力を必要とするので、入力Stringは、いかなる意味のある方法で使用することができないということです。

シングルトンでないランタイムデータがある場合は、それをtoSingという実在シングルトンに変換することができます。次に、結果のシングルトンのプロパティについて学習するためにランタイムチェックを実行することができます。

+0

このチュートリアルは、私がこれをしようとしてくれたものです:https://blog.jle.im/entry/practical-dependent-types-in-haskell-2.html彼はそれを使用して実行時に型を生成します。しかし、それは 'SomeSing'部分の周りに私を失った。私はそれがどのように使用されることが意図されているのか直感を見つけることはできません。 –

+0

直感は、GHCのシングルトンが複雑で不都合な近似であるため、理想的には依存型理論から来るべきです。しかし、初心者のマテリアルはあまりありません。 [こちら](https://www.manning.com/books/type-driven-development-with-idris)は良いようですが、無料ではありません。あなたは[これらの](http://people.inf.elte.hu/divip/AgdaTutorial/Index.html)[two](http://oxij.org/note/BrutalDepTypes/)Agdaチュートリアルを見ることができますが、プログラミングよりもフォーマル化に向いています。 –

+0

また、[ソフトウェア基盤](https://www.cis.upenn.edu/~bcpierce/sf/current/index.html)も見ることができます。これは非常に徹底的で初心者向きですが、プルーフにも焦点を当てていますCoqはIdrisやAgdaよりもHaskellersにとってより疎外です。 –

関連する問題