Haskell 从字符串读取并转换为动态

Haskell 从字符串读取并转换为动态,haskell,Haskell,我想实现如下功能: readToDyn::String->Maybe Dynamic readToDyn x = readMaybe x >>= pure . toDyn 它将字符串转换为特定类型,然后将其打包为动态类型。问题是readMaybe::Read a=>String->Maybe a中的类型变量a不明确,因此无法编译 在上面定义函数时,有没有办法忽略这种不明确的情况,以便我可以在调用fromDynamic时指定类型?像 动态是一种存在类型。这意味着,每个包装的值实

我想实现如下功能:

readToDyn::String->Maybe Dynamic
readToDyn x = readMaybe x >>= pure . toDyn
它将字符串转换为特定类型,然后将其打包为动态类型。问题是readMaybe::Read a=>String->Maybe a中的类型变量a不明确,因此无法编译

在上面定义函数时,有没有办法忽略这种不明确的情况,以便我可以在调用fromDynamic时指定类型?像

动态是一种存在类型。这意味着,每个包装的值实际上属于一个具体的“静态类型”,在检查它之前,您不知道是哪个

data Dynamic where
    Dynamic :: ∀ a. TypeRep a -> a -> Dynamic
       -- i.e. ∀ a.(TypeRep a -> a -> Dynamic)
这使用了一个适用于所有人的quantor,但可以说,用一个在Haskell中目前不合法的存在主义quantor来理解它会更好:

data Dynamic where
    Dynamic :: (∃ a. (TypeRep a, a)) -> Dynamic
请注意,quantor现在位于构造函数的参数中,而之前它位于整个构造函数中

您想要的是一种通用类型,即基础值本身不知道它将是什么类型,但可以适应调用站点所需的任何类型。这用参数中的通用量词表示:

data AnyReadable where
  AnyReadable :: (∀ a. Read a => a) -> AnyReadable
但是,这对于读取并不起作用,至少对于字符串->可能是AnyReadable不起作用,因为在确定是否存在解析错误之前,您需要知道要读取它的类型

动态是一种存在类型。这意味着,每个包装的值实际上属于一个具体的“静态类型”,在检查它之前,您不知道是哪个

data Dynamic where
    Dynamic :: ∀ a. TypeRep a -> a -> Dynamic
       -- i.e. ∀ a.(TypeRep a -> a -> Dynamic)
这使用了一个适用于所有人的quantor,但可以说,用一个在Haskell中目前不合法的存在主义quantor来理解它会更好:

data Dynamic where
    Dynamic :: (∃ a. (TypeRep a, a)) -> Dynamic
请注意,quantor现在位于构造函数的参数中,而之前它位于整个构造函数中

您想要的是一种通用类型,即基础值本身不知道它将是什么类型,但可以适应调用站点所需的任何类型。这用参数中的通用量词表示:

data AnyReadable where
  AnyReadable :: (∀ a. Read a => a) -> AnyReadable

但是,这对读取不起作用,至少对String->AnyReadable不起作用,因为在确定是否存在解析错误之前,您需要知道要读取的类型。

简单的解决方案是只存储字符串,然后再解析它

在构建动态值时,如果不知道稍后使用fromDynamic时将需要哪种类型,则无法解析字符串。在此阶段无法执行任何有用的预处理

原则上,您可以使用通用限定类型,例如:

data Parsed = Parsed { parse :: forall a . Read a => Maybe a }

readToDyn :: String -> Dynamic
readToDyn x = toDyn (Parsed (readMaybe x))

-- ...
do let x = readToDyn someString
   -- ...
   use (parse (fromDynamic x) :: Maybe Int)

但这毫无意义地复杂。注意readToDyn不返回Maybe Dynamic,因为这里没有办法知道以后解析是否会失败,这反映出我们不知道需要解析Int。

简单的解决方案是只存储字符串,然后再解析它

在构建动态值时,如果不知道稍后使用fromDynamic时将需要哪种类型,则无法解析字符串。在此阶段无法执行任何有用的预处理

原则上,您可以使用通用限定类型,例如:

data Parsed = Parsed { parse :: forall a . Read a => Maybe a }

readToDyn :: String -> Dynamic
readToDyn x = toDyn (Parsed (readMaybe x))

-- ...
do let x = readToDyn someString
   -- ...
   use (parse (fromDynamic x) :: Maybe Int)

但这毫无意义地复杂。注意readToDyn不会返回Maybe Dynamic,因为这里无法知道解析是否会在以后失败,这反映出我们不知道我们将需要解析Int。

您的问题的另一种解释是:您想从字符串中确定要使用的具体类型。即readToDyn 4应给出一个整数,readToDyn True应给出一个布尔值,等等。。这样的方法总是不完整的,但是如果您只需要它来处理有限的类型集合,那么这是可以的。基本上,你会这样做:

{-# LANGUAGE TypeApplications #-}

import Control.Applicative ((<|>))

readToDyn :: String -> Maybe Dynamic
readToDyn s = (toDyn @Integer <$> readMaybe s)
          <|> (toDyn @Bool <$> readMaybe s)
          <|> ...
更优雅/可扩展的方法:

{-# LANGUAGE TypeApplications, DataKinds, TypeInType
           , AllowAmbiguousTypes, UnicodeSyntax, ScopedTypeVariables #-}

import Data.Kind (Type)

class DynRead (ts :: [Type]) where
   readToDyn :: String -> Maybe Dynamic

instance DynRead ('[]) where
   readToDyn _ = Nothing
instance ∀ h t . (Read h, Typeable h, DynRead t) => DynRead (h ': t) where
   readToDyn s = toDyn @h <$> readMaybe s
                    <|> readToDyn @t s

对您的问题的另一种解释是:您想从字符串中确定要使用的具体类型。即readToDyn 4应给出一个整数,readToDyn True应给出一个布尔值,等等。。这样的方法总是不完整的,但是如果您只需要它来处理有限的类型集合,那么这是可以的。基本上,你会这样做:

{-# LANGUAGE TypeApplications #-}

import Control.Applicative ((<|>))

readToDyn :: String -> Maybe Dynamic
readToDyn s = (toDyn @Integer <$> readMaybe s)
          <|> (toDyn @Bool <$> readMaybe s)
          <|> ...
更优雅/可扩展的方法:

{-# LANGUAGE TypeApplications, DataKinds, TypeInType
           , AllowAmbiguousTypes, UnicodeSyntax, ScopedTypeVariables #-}

import Data.Kind (Type)

class DynRead (ts :: [Type]) where
   readToDyn :: String -> Maybe Dynamic

instance DynRead ('[]) where
   readToDyn _ = Nothing
instance ∀ h t . (Read h, Typeable h, DynRead t) => DynRead (h ': t) where
   readToDyn s = toDyn @h <$> readMaybe s
                    <|> readToDyn @t s