from 1.0.0.0 → 1.0.0.1
raw patch · 5 files changed
+147/−95 lines, 5 filesdep +fromdep ~bytestringdep ~rawfilepathdep ~unixPVP ok
version bump matches the API change (PVP)
Dependencies added: from
Dependency ranges changed: bytestring, rawfilepath, unix
API changes (from Hackage documentation)
Files
- CHANGELOG.md +8/−4
- README.md +8/−3
- codegen/Main.hs +90/−0
- from.cabal +27/−5
- test/Main.hs +14/−83
CHANGELOG.md view
@@ -4,16 +4,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). -## Unreleased+## 1.0.0.1 - 2025-10-24 -(no changes yet)+### Changed +- Split the test code into ordinary test and code generation test. The code generation test will not be run by default (by Hackage or Stackage, for example).+ ## 1.0.0.0 - 2025-10-24 Initial release. -- Added typeclasses `From` and `TryFrom`.-- Added instances of `From` for some fundamental type-mappings in `base`:+### Added++- Typeclasses `From` and `TryFrom`.+- Instances of `From` for some fundamental type-mappings in `base`: - All types in `Data.Int` and `Data.Word` can be both the source and the destination of the conversion. - The types `Float` and `Double` can be the destination, but not the source. So `from :: Int32 -> Float` exists, while `from :: Float -> Int32` does not. - That's it. We begin with minimal instances intentionally. For example, there is no `instance From Int String`. This is because it is tricky (often impossible) to selectively import instances, especially when the module dependency graph is complicated. We'd need to be careful about adding instances.
README.md view
@@ -12,10 +12,15 @@ ## Instances -It is well known that it is difficult to "selectively" import/export typeclass instances. Therefore, we need to be careful about what instances to provide by default.+It is difficult and often impossible to "selectively" import/export typeclass instances. Therefore, we need to be careful about what instances to provide by default. -There are base functions, some of them even in `Prelude`, that basically serve as a less general version of `from`. Examples include `fromIntegral` and `fromEnum`. However, naively defining `instance (Integral a, Num b) => From a b` and `instance (Enum a) => From a Int` will quickly lead us to trouble:+There are base functions, some of them even in `Prelude`, that basically serve as a less general version of `from`. Examples include `fromIntegral` and `fromEnum`. However, naively defining +- `instance (Integral a, Num b) => From a b` and+- `instance (Enum a) => From a Int`++will quickly lead us to trouble:+ ``` • Overlapping instances for From Int16 Int arising from a use of ‘from’@@ -25,7 +30,7 @@ ... ``` -Therefore, this package does not provide such "contextual" instances. (Of course, you may define them in your application project, if you want.)+Therefore, this package does not provide such "contextual" instances. Instead, concrete instances are provided. For example, all pairs (excluding self to self) of these integral types are instantiated using `fromIntegral`:
+ codegen/Main.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Control.Exception+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.ByteString.Builder (Builder)+import qualified Data.ByteString.Builder as B+import RawFilePath+import System.Exit+import System.IO+import System.Posix.Env.ByteString+import System.Posix.Temp.ByteString++srcTypes :: [ByteString]+srcTypes =+ [ "Int"+ , "Integer"+ , "Int8"+ , "Int16"+ , "Int32"+ , "Int64"+ , "Word8"+ , "Word16"+ , "Word32"+ , "Word64"+ ]++dstTypes :: [ByteString]+dstTypes =+ [ "Int"+ , "Integer"+ , "Int8"+ , "Int16"+ , "Int32"+ , "Int64"+ , "Word8"+ , "Word16"+ , "Word32"+ , "Word64"+ , "Float"+ , "Double"+ ]++main :: IO ()+main = do+ args <- getArgs+ let shouldKeepFile = "--keep" `elem` args+ bracket acquire (release shouldKeepFile) $ \(tmpPath, hdl) -> do+ B.putStr $ tmpPath <> "\n"+ B.hPutBuilder hdl $ header <> mconcat instancePart+ hClose hdl+ diffExitCode <-+ runProcess "diff" ["-u", "--color=always", tmpPath, "src/From/Num.hs"]+ case diffExitCode of+ ExitSuccess -> return ()+ _ -> exitWith diffExitCode+ where+ acquire = mkstemps "haskell-from-" ".hs"+ release shouldKeepFile (path, hdl)+ | shouldKeepFile = hClose hdl+ | otherwise = hClose hdl >> removeFile path++ runProcess cmd args = startProcess (proc cmd args) >>= waitForProcess++ instanceLine src dst =+ mconcat ["instance From ", B.byteString src, " ", B.byteString dst, " where\n"]+ instancePart =+ [ mconcat+ [ "\n"+ , "-- | Implementation is 'fromIntegral'\n"+ , instanceLine src dst+ , " from = fromIntegral\n"+ ]+ | src <- srcTypes+ , dst <- dstTypes+ , src /= dst+ ]++header :: Builder+header =+ "{-# LANGUAGE MultiParamTypeClasses #-}\n\+ \{-# OPTIONS_GHC -Wno-orphans #-}\n\+ \\n\+ \module From.Num () where\n\+ \\n\+ \import Data.Int\n\+ \import Data.Word\n\+ \import From.Classes\n"
from.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: from-version: 1.0.0.0+version: 1.0.0.1 synopsis: Typeclasses for type conversion mappings description: This package provides the typeclasses 'From' and 'TryFrom'. .@@ -41,6 +41,11 @@ type: git location: https://codeberg.org/xt/from +flag codegen+ description: Enable code generation test+ manual: True+ default: False+ library exposed-modules: From@@ -55,17 +60,34 @@ base >=4.7 && <5 default-language: Haskell2010 -test-suite from-test+test-suite codegen type: exitcode-stdio-1.0 main-is: Main.hs other-modules: Paths_from hs-source-dirs:+ codegen+ ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+ build-depends:+ base >=4.7 && <5+ , bytestring >=0.11.4.0 && <2+ , rawfilepath >=1.0.1 && <2+ , unix >2.7.3 && <3+ default-language: Haskell2010+ if flag(codegen)+ buildable: True+ else+ buildable: False++test-suite test+ type: exitcode-stdio-1.0+ main-is: Main.hs+ other-modules:+ Paths_from+ hs-source-dirs: test ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints build-depends: base >=4.7 && <5- , bytestring- , rawfilepath- , unix+ , from default-language: Haskell2010
test/Main.hs view
@@ -1,90 +1,21 @@-{-# LANGUAGE OverloadedStrings #-}- module Main (main) where -import Control.Exception-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import Data.ByteString.Builder (Builder)-import qualified Data.ByteString.Builder as B-import RawFilePath+import Data.Int+import Data.Word+import From import System.Exit-import System.IO-import System.Posix.Env.ByteString-import System.Posix.Temp.ByteString -srcTypes :: [ByteString]-srcTypes =- [ "Int"- , "Integer"- , "Int8"- , "Int16"- , "Int32"- , "Int64"- , "Word8"- , "Word16"- , "Word32"- , "Word64"- ]--dstTypes :: [ByteString]-dstTypes =- [ "Int"- , "Integer"- , "Int8"- , "Int16"- , "Int32"- , "Int64"- , "Word8"- , "Word16"- , "Word32"- , "Word64"- , "Float"- , "Double"- ]+assertEq :: (Eq term) => String -> term -> term -> IO ()+assertEq comment leftTerm rightTerm+ | leftTerm == rightTerm = report+ | otherwise = report >> exitFailure+ where+ report = putStrLn comment +-- TODO: More test cases main :: IO () main = do- args <- getArgs- let shouldKeepFile = "--keep" `elem` args- bracket acquire (release shouldKeepFile) $ \(tmpPath, hdl) -> do- B.putStr $ tmpPath <> "\n"- B.hPutBuilder hdl $ header <> mconcat instancePart- hClose hdl- diffExitCode <-- runProcess "diff" ["-u", "--color=always", tmpPath, "src/From/Num.hs"]- case diffExitCode of- ExitSuccess -> return ()- _ -> exitWith diffExitCode- where- acquire = mkstemps "haskell-from-" ".hs"- release shouldKeepFile (path, hdl)- | shouldKeepFile = hClose hdl- | otherwise = hClose hdl >> removeFile path-- runProcess cmd args = startProcess (proc cmd args) >>= waitForProcess-- instanceLine src dst =- mconcat ["instance From ", B.byteString src, " ", B.byteString dst, " where\n"]- instancePart =- [ mconcat- [ "\n"- , "-- | Implementation is 'fromIntegral'\n"- , instanceLine src dst- , " from = fromIntegral\n"- ]- | src <- srcTypes- , dst <- dstTypes- , src /= dst- ]--header :: Builder-header =- "{-# LANGUAGE MultiParamTypeClasses #-}\n\- \{-# OPTIONS_GHC -Wno-orphans #-}\n\- \\n\- \module From.Num () where\n\- \\n\- \import Data.Int\n\- \import Data.Word\n\- \import From.Classes\n"+ assertEq+ "round trip from Int8 to Word32"+ (0x11 :: Int8)+ (from (from (0x11 :: Int8) :: Word32))