Skip to content

Commit

Permalink
Improved Lift instances for Path/OsPath types
Browse files Browse the repository at this point in the history
The `Lift` instances support now parametic types and promoted data
constructors for the two parameters of the Path/OsPath types.
  • Loading branch information
mmhat committed Sep 11, 2024
1 parent 17294d1 commit 5169212
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 18 deletions.
2 changes: 2 additions & 0 deletions path.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ library
, OsPath.Internal.Posix
, OsPath.Internal.Windows

other-modules: Utils

build-depends: aeson >= 1.0.0.0
, base >= 4.12 && < 5
, deepseq
Expand Down
12 changes: 3 additions & 9 deletions src/OsPath/Internal/Include.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import qualified System.OsPath.PLATFORM_NAME as OsPath

import System.OsString.Compat.PLATFORM_NAME (PLATFORM_STRING)
import qualified System.OsString.Compat.PLATFORM_NAME as OsString
import Utils (typeableToType)

-- | Path of some base and type.
--
Expand Down Expand Up @@ -113,16 +114,9 @@ instance Hashable (Path b t) where

instance forall b t. (Typeable b, Typeable t) => TH.Lift (Path b t) where
lift (Path str) = do
let b = TH.ConT $ getTCName (Proxy :: Proxy b)
t = TH.ConT $ getTCName (Proxy :: Proxy t)
let b = typeableToType (Proxy :: Proxy b)
t = typeableToType (Proxy :: Proxy t)
[| Path $(TH.lift str) :: Path $(pure b) $(pure t) |]
where
getTCName :: Typeable a => proxy a -> TH.Name
getTCName a = TH.Name occ flav
where
tc = typeRepTyCon (typeRep a)
occ = TH.OccName (tyConName tc)
flav = TH.NameG TH.TcClsName (TH.PkgName (tyConPackage tc)) (TH.ModName (tyConModule tc))

#if MIN_VERSION_template_haskell(2,17,0)
liftTyped = TH.unsafeCodeCoerce . TH.lift
Expand Down
13 changes: 4 additions & 9 deletions src/Path/Internal/Include.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import qualified Data.List as L
import qualified Language.Haskell.TH.Syntax as TH
import qualified System.FilePath.PLATFORM_NAME as FilePath

import Utils (typeableToType)

-- | Path of some base and type.
--
-- The type variables are:
Expand Down Expand Up @@ -121,16 +123,9 @@ instance Hashable (Path b t) where

instance forall b t. (Typeable b, Typeable t) => TH.Lift (Path b t) where
lift (Path str) = do
let b = TH.ConT $ getTCName (Proxy :: Proxy b)
t = TH.ConT $ getTCName (Proxy :: Proxy t)
let b = typeableToType (Proxy :: Proxy b)
t = typeableToType (Proxy :: Proxy t)
[|Path $(pure (TH.LitE (TH.StringL str))) :: Path $(pure b) $(pure t) |]
where
getTCName :: Typeable a => proxy a -> TH.Name
getTCName a = TH.Name occ flav
where
tc = typeRepTyCon (typeRep a)
occ = TH.OccName (tyConName tc)
flav = TH.NameG TH.TcClsName (TH.PkgName (tyConPackage tc)) (TH.ModName (tyConModule tc))

#if MIN_VERSION_template_haskell(2,17,0)
liftTyped = TH.unsafeCodeCoerce . TH.lift
Expand Down
43 changes: 43 additions & 0 deletions src/Utils.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{-# LANGUAGE TypeApplications #-}

module Utils
( typeableToType
) where

import Data.Bifunctor (first)
import Data.Kind (Type)
import qualified Data.List as List
import Data.Typeable (splitTyConApp)
import qualified Language.Haskell.TH.Syntax as TH
import Type.Reflection

typeableToType :: (Typeable a) => proxy a -> TH.Type
typeableToType = typeRepToType . someTypeRep

typeRepToType :: SomeTypeRep -> TH.Type
typeRepToType rep =
uncurry (foldl' f)

Check failure on line 19 in src/Utils.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.8.2

Variable not in scope:
. first tyConToType
. splitTyConApp
$ rep
where
f :: TH.Type -> SomeTypeRep -> TH.Type
f memo = TH.AppT memo . typeRepToType

tyConToType :: TyCon -> TH.Type
tyConToType tc =
(if isType then TH.ConT else TH.PromotedT)
( TH.Name
(TH.OccName (List.dropWhile (== '\'') (tyConName tc)))
( TH.NameG
(if isType then TH.TcClsName else TH.DataName)
(TH.PkgName (tyConPackage tc))
(TH.ModName (tyConModule tc))
)
)

isType :: Bool
isType = someTypeRepKind rep == SomeTypeRep (typeRep @Type)

someTypeRepKind :: SomeTypeRep -> SomeTypeRep
someTypeRepKind (SomeTypeRep rep) = SomeTypeRep (typeRepKind rep)
5 changes: 5 additions & 0 deletions test-ospath/TH/Include.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
-- PLATFORM_NAME = Posix | Windows
-- PLATFORM_PATH = PosixPath | WindowsPath

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
Expand All @@ -11,6 +12,7 @@
-- | Test functions to check the template haskell bits.
module TH.PLATFORM_NAME where

import Data.Proxy (Proxy)
import qualified Language.Haskell.TH.Syntax as TH
import System.OsPath.PLATFORM_NAME (PLATFORM_PATH)

Expand Down Expand Up @@ -48,3 +50,6 @@ liftRelDir = checkInstantiated $(TH.lift (Path [OsString.pstr|name/|] :: Path Re

liftRelFile :: PLATFORM_PATH
liftRelFile = checkInstantiated $(TH.lift (Path [OsString.pstr|name|] :: Path Rel File))

liftComplex :: PLATFORM_PATH
liftComplex = toOsPath $(TH.lift (Path [OsString.pstr|name|] :: Path [[Bool]] (Proxy 'True)))
5 changes: 5 additions & 0 deletions test/TH/Include.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-- This template expects CPP definitions for:
-- PLATFORM_NAME = Posix | Windows

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
Expand All @@ -9,6 +10,7 @@
-- | Test functions to check the template haskell bits.
module TH.PLATFORM_NAME where

import Data.Proxy (Proxy)
import qualified Language.Haskell.TH.Syntax as TH

import Path.Internal.PLATFORM_NAME
Expand Down Expand Up @@ -44,3 +46,6 @@ liftRelDir = checkInstantiated $(TH.lift (Path "name/" :: Path Rel Dir))

liftRelFile :: FilePath
liftRelFile = checkInstantiated $(TH.lift (Path "name" :: Path Rel File))

liftComplex :: FilePath
liftComplex = toFilePath $(TH.lift (Path "name" :: Path [[Bool]] (Proxy 'True)))

0 comments on commit 5169212

Please sign in to comment.