Есть несколько способов сделать это, хотя, как говорит @DanielWagner, трудно сказать, что будет работать лучше для вас, без дополнительной информации о том, чего вы пытаетесь достичь.
Самым простым является, вероятно,используйте класс типов со связанным семейством типов (или многопараметрический класс типов с функциональной зависимостью), чтобы сопоставить тип оболочки файла с подтипом компилятора.Подход семейства типов выглядит следующим образом:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
class Loadable a where
filepath :: a -> String
type Load a
с экземплярами шаблонов, такими как:
instance Loadable SourceFilepath where
filepath (SourceFilepath pth) = pth
type Load SourceFilepath = Source
instance Loadable HeaderFilepath where
filepath (HeaderFilepath pth) = pth
type Load HeaderFilepath = Header
instance Loadable MetadataFilepath where
filepath (MetadataFilepath pth) = pth
type Load MetadataFilepath = Metadata
Обратите внимание, что здесь нет проблем с отображением двух оболочек путей к одному и тому же подтипу компилятора (например,type Load HeaderFilepath = Source
будет работать нормально).
Дано:
subload :: FromJSON b => FilePath -> Compiler b
subload = ...
определение loadSource
:
loadSource :: (Loadable a, FromJSON (Load a)) => a -> Compiler (Load a)
loadSource = subload . filepath
, после чего:
> :t loadSource (SourceFilepath "bob")
loadSource (SourceFilepath "bob") :: Compiler Source
> :t loadSource (MetadataFilepath "alice")
loadSource (MetadataFilepath "alice") :: Compiler Metadata
Вы можете существенно уменьшить шаблон, параметризовав обертку, и - как @DanielWagner - я не понимаю ваш комментарий о том, что компилятор рассматривает их как файлы того же типа, поэтому вам нужно показать нам, чточто-то не так, когда вы пытаетесь это сделать.
В любом случае, мой полный источник исходного решения семейства типов:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wall #-}
import Data.Aeson
import GHC.Generics
newtype SourceFilepath = SourceFilepath String deriving (Show)
newtype HeaderFilepath = HeaderFilepath String deriving (Show)
newtype MetadataFilepath = MetadataFilepath String deriving (Show)
data Source = Source deriving (Generic)
data Header = Header deriving (Generic)
data Metadata = Metadata deriving (Generic)
instance FromJSON Source
instance FromJSON Header
instance FromJSON Metadata
data Compiler b = Compiler
subload :: FromJSON b => FilePath -> Compiler b
subload = undefined
class Loadable a where
filepath :: a -> String
type Load a
instance Loadable SourceFilepath where
filepath (SourceFilepath pth) = pth
type Load SourceFilepath = Source
instance Loadable HeaderFilepath where
filepath (HeaderFilepath pth) = pth
type Load HeaderFilepath = Header
instance Loadable MetadataFilepath where
filepath (MetadataFilepath pth) = pth
type Load MetadataFilepath = Metadata
loadSource :: (Loadable a, FromJSON (Load a)) => a -> Compiler (Load a)
loadSource = subload . filepath
и полный источник тегового решения:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wall #-}
import Data.Aeson
import GHC.Generics
newtype TypedFilePath a = TypedFilePath FilePath deriving (Show)
data Source = Source deriving (Generic)
data Header = Header deriving (Generic)
data Metadata = Metadata deriving (Generic)
instance FromJSON Source
instance FromJSON Header
instance FromJSON Metadata
data Compiler b = Compiler
subload :: FromJSON b => FilePath -> Compiler b
subload = undefined
type family Load a where
Load Source = Source
Load Header = Header
Load Metadata = Metadata
loadSource :: FromJSON (Load a) => TypedFilePath a -> Compiler (Load a)
loadSource (TypedFilePath fn) = subload fn