3

Haskellで動的型付け言語のインタプリタを書いています。

ほとんどの動的型付け言語インタープリターと同様に、私のプログラムも実行時に型をチェックする必要があります。私のプログラムで最もよく使われるコードの1つはこれです:

interpreterFunction :: Value -> SomeMonadicContext ReturnType
interpreterFunction p = do
    VStr s <- ensureType p TString
    ..
    some code that uses s

ここでは、それpがタイプTStringであることを確認し、その後、でそれを破棄していVStr s <- ...ます。VStrタイプがの唯一の値であるため、これが失敗することはありませんTString

私のデータ構造は基本的にこれです:

data Value = VStr String | VInt Int | VBool Bool
data Type  = TStr | TInt | TBool

そのため、タイプに応じて値を分離しています。すなわち。型として持つ値コンストラクターは1つだけTStrです。

今、私は自分のensureType関数と破壊コードを単純化する方法があるかどうか疑問に思っています。たとえば、そのようなことは可能ですか?

interpreterFunction p = do
    s <- ensureType p
    ..
    same code that uses s

ここで、タイプs <-がであると推定できる後のコードから、部分を持つコンストラクターのみがであることが静的にわかっているため、がであるかどうかを動的にチェックした後に戻ります。sStringValueStringVStrensureTypeStringpVStr

これが理にかなっているのか、それとも可能なのか、私には実際にはわかりません。Haskellの高度な型システム機能を利用して設計を改善する方法を探しています。

どんな助けでもありがたいです。

4

2 に答える 2

3

はい、実際には型クラスの助けを借りてそれを行うことができます。それが賢明であるかどうかは議論の余地があります(あなたの単純なValueタイプの場合、パターンマッチングはおそらくより良い解決策です)が、それでも興味深いです:)

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Main where

data Value = VStr String | VInt Int | VBool Bool

class FromValue a where
    fromValue :: Value -> Maybe a
instance FromValue String where
    fromValue (VStr s) = Just s
    fromValue _ = Nothing

ensureType :: (Monad m, FromValue a) => Value -> m a
ensureType = maybe (fail "type error!") return . fromValue

interpreterFunction :: Value -> IO ()
interpreterFunction val = 
    do s <- ensureType val
       putStrLn s

main =
    do interpreterFunction (VStr "asd")
       interpreterFunction (VInt 1)

プリント:

asd
*** Exception: user error (type error!)

拡張機能を利用して、ScopedTypeVariables推測できない場合に特定のタイプを強制することもできます。

{-# LANGUAGE ScopedTypeVariables #-}
interpreterFunction2 :: Value -> IO ()
interpreterFunction2 val = 
    do (s :: String) <- ensureType val
       print s

ちなみに、あなたの最初のアプローチは私には少し「不健全」に思えます。

VStr s <- ensureType p TString

(内省によって)文字列以外のものを返さないことは確かかもしれませんがensureType x TString、これは型システムによって強制されることはなく、パターン一致は網羅的ではありません。これはここでは大きな問題ではありませんが、代わりに特別な「文字列抽出」関数を使用することで、その関数の実行時エラーの可能性を簡単に根絶することができます。

ensureString :: (Monad m) => Value -> m String
{- ... -}
s <- ensureString val
于 2013-02-25T00:04:23.540 に答える
2

1.クリーンで明確な簡単な方法:

あなたが定義するタグ付き共用体のタイプだと思います

data Value = VStr String | VInt Int | VBool Bool

必要なすべてのランタイム型チェックが単純な古いパターンマッチングとして組み込まれており、それをいくつかの高度な型システム機能でラップすると、クリーンで明確な解決策が欠けています。

interpreterFunction :: Value -> SomeMonadicContext ReturnType
interpreterFunction (Vstr s) = do
    some code that uses s
interpreterFunction _ = do
    some error handling code

あなたが好きだった:

interpreterFunction p = do
    s <- ensureType p
    ..
    same code that uses s

言って

"ここで、s <-の後のコードから、sの型はStringであると推測できます。静的には、String部分を持つValueコンストラクターのみがVStrであることがわかっているため、ensureTypeは、pがVStrであるかどうかを動的にチェックした後にStringを返します。

私のバージョンでは、pがVStrであるかどうかも動的にチェックされます。文字列部分を持つ唯一のValueコンストラクターがVStrであることが静的に知られていますが、実際にはそれを悪用するのは非常に困難です。

2.あまりクリーンでない型クラスベースの方法

Stringインスタンスを作成する必要があるため、

{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}

class FromValue a where 
   ensureType :: Value -> SomeMonadicContext a

インスタンスの例:

instance FromValue String where
   ensureType (VStr s) = return s
   ensureType  _       = fail $ unlines 
                          ["Yikes, I'd rather have thrown a Nothing than a fail,"
                          ,"but then I'd have to have detagged the Just later"
                          ,"and then I may as well have used solution 1 anyway!"]

instance FromValue Int where
   ensureType (VInt i) = return i
   ensureType  _       = fail "Oh no, do I really have to use catch?"

どちらが

interpreterFunction :: Value -> IO String
interpreterFunction p = do
    s <- ensureType p
    return $ s ++ ", yay!" -- example String-specific code.

これには、ユーザーへの適切な応答を選択する代わりに失敗するという欠点があります。を使用catchして回復することもできますが、パターンマッチングソリューションが提供するカスタマイズ可能なケース処理機能を複製するためだけに、非常に多くの作業を行っています。

2b。多分?

多分データ型を使用するensureTypeと、煩わしい失敗/キャッチを回避できますが 、関数を実行Just s <- ensureType pまたは使用する必要がありmaybeます。少なくとも、プレーンパターンマッチと同じくらいの作業が必要です。

3.何か賢くて新しいが不適切

あるいは、クールで新しい型システム機能を使用することが主な目的である場合は、GADTを使用して存在型を複製するか、Dynamicを使用して、より構造化された動的型付けのような方法で複製することができますが、そうではありません。これらのどちらにも、単純なパターンマッチングの明快さ、単純さ、優雅さはありません。

于 2013-02-25T02:21:54.850 に答える