|
5 | 5 | -- Maintainer : streamly@composewell.com |
6 | 6 | -- Portability : GHC |
7 | 7 | -- |
8 | | --- File system paths with flexible (gradual) typing, extensible, |
| 8 | +-- File system paths supporting flexible (gradual) typing; extensible, |
9 | 9 | -- high-performance, preserving the OS and filesystem encoding. |
10 | 10 | -- |
11 | | --- /Flexible/: you can choose the level of type safety you want. 'Path' is the |
12 | | --- basic path type which can represent a file, directory, absolute or relative |
13 | | --- path with no restrictions. Depending on how much type safety you want, you |
14 | | --- can choose appropriate type wrappers or a combination of those to wrap the |
15 | | --- 'Path' type. |
| 11 | +-- /Flexible typing/: you can choose the level of type safety you want. 'Path' |
| 12 | +-- is the basic path type which can represent a file, directory, absolute or |
| 13 | +-- relative path with no restrictions. Depending on how much type safety you |
| 14 | +-- want, you can choose appropriate type wrappers or a combination of those to |
| 15 | +-- wrap the 'Path' type in stricter types. |
16 | 16 | -- |
17 | 17 | -- = Rooted Paths vs Branches |
18 | 18 | -- |
19 | | --- For the safety of the path append operation we make the distinction of |
20 | | --- rooted paths vs branches. A path that starts from some implicit or |
21 | | --- explicit root in the file system is a rooted path, for example, @\/usr\/bin@ |
22 | | --- is a rooted path starting from an explicit file system root directory @/@. |
23 | | --- Similarly, @.\/bin@ is a path with an implicit root, this path is hanging |
24 | | --- from the current directory. A path that is not rooted is called a branch |
25 | | --- e.g. @local\/bin@ is a branch. |
26 | | --- |
27 | | --- This distinction affords safety to the path append operation. We can always |
28 | | --- append a branch to a rooted path or to another branch. However, it does |
29 | | --- not make sense to append a rooted path to another rooted path. The default |
| 19 | +-- To ensure the safety of the append operation, we distinguish between |
| 20 | +-- rooted paths and branch-type paths. A path that starts from an explicit or |
| 21 | +-- implicit root in the file system is called a rooted path. For example, |
| 22 | +-- @\/usr\/bin@ is a rooted path starting from the explicit root directory @/@. |
| 23 | +-- Similarly, @.\/bin@ is rooted implicitly, hanging from the current directory. |
| 24 | +-- A path that is not rooted is called a branch; for example, @local\/bin@ is a |
| 25 | +-- branch. |
| 26 | +-- |
| 27 | +-- This distinction ensures the safety of the path append operation. You can |
| 28 | +-- always append a branch to a rooted path or to another branch. However, |
| 29 | +-- it does not make sense to append one rooted path to another. The default |
30 | 30 | -- append operation in the Path module checks for this and fails if the |
31 | | --- operation is incorrect. However, the programmer can force it by using the |
32 | | --- unsafe version of the append operation. You can also drop the root |
33 | | --- explicitly and use the safe append operation. |
34 | | --- |
35 | | --- The "Streamly.FileSystem.Path.Seg" module provides explicit typing of path |
36 | | --- segments e.g. rooted paths vs branches. Rooted paths are represented by the |
37 | | --- @Rooted Path@ type and branches are represented by the @Branch Path@ type. |
38 | | --- If you use the 'Path' type then append can fail if you try to append a |
39 | | --- rooted path to another path, but if you use @Rooted Path@ and @Branch Path@ |
40 | | --- types then append can never fail at run time as the types would not allow it |
41 | | --- at compile time. |
42 | | --- |
43 | | --- = Absolute vs Relative Rooted Paths |
44 | | --- |
45 | | --- Rooted paths can be absolute or relative. Absolute paths have an absolute |
46 | | --- root e.g. @\/usr\/bin@. Relative paths have a dynamic or relative root e.g. |
47 | | --- @.\/local\/bin@, or @.@, in these cases the root is current directory which |
48 | | --- is not absolute but can change dynamically. Note that there is no type level |
49 | | --- distinction for absolute and relative paths. The append operation requires a |
50 | | --- distinction between Rooted and Branch only. |
51 | | --- |
52 | | --- = File vs Directory Paths |
53 | | --- |
54 | | --- Independent of the rooted or branch distinction you can also make a type |
55 | | --- level distinction between file and directory type nodes using the |
56 | | --- "Streamly.FileSystem.Path.Node" module. @File Path@ type represents a file |
57 | | --- whereas @Dir Path@ represents a directory. This distinction provides safety |
58 | | --- against appending a path to a file. Append operation does not allow |
59 | | --- appending to 'File' types. |
60 | | --- |
61 | | --- By default a path with a trailing separator is implicitly considered a |
62 | | --- directory path. However, the absence of a trailing separator does not convey |
63 | | --- any information, it could either be a directory or a file. Thus the append |
64 | | --- operation allows appending to even paths that do not have a trailing |
65 | | --- separator. However, when creating a typed path of 'File' type the conversion |
66 | | --- fails unless we explicitly drop the trailing separator. |
| 31 | +-- operation is invalid. However, the programmer can force it using the |
| 32 | +-- unsafe append operation. Alternatively, you can drop the root explicitly |
| 33 | +-- and use the safe append. |
| 34 | +-- |
| 35 | +-- The "Streamly.FileSystem.Path.Seg" module provides explicit types for path |
| 36 | +-- segments, distinguishing rooted paths from branches. Rooted paths use the |
| 37 | +-- @Rooted Path@ type, and branches use the @Branch Path@ type. If you use the |
| 38 | +-- generic 'Path' type, append may fail at run time if you attempt to append |
| 39 | +-- a rooted path to another rooted path. In contrast, using the @Rooted Path@ |
| 40 | +-- and @Branch Path@ types guarantees compile-time safety, preventing such errors. |
| 41 | +-- |
| 42 | +-- Since we distinguish between rooted and branch-type paths, a separate |
| 43 | +-- distinction between absolute and relative paths is not required. Both are |
| 44 | +-- considered rooted paths, and all rooted paths are protected from invalid |
| 45 | +-- append operations. Only branch-type paths can be appended. |
| 46 | +-- |
| 47 | +-- = File vs. Directory Paths |
| 48 | +-- |
| 49 | +-- Independent of the rooted or branch distinction, you can also make a |
| 50 | +-- type-level distinction between file and directory nodes using the |
| 51 | +-- "Streamly.FileSystem.Path.Node" module. The type @File Path@ represents a |
| 52 | +-- file, whereas @Dir Path@ represents a directory. This distinction provides |
| 53 | +-- safety against appending to file type paths — append operations are not |
| 54 | +-- allowed on paths of type 'File'. |
| 55 | +-- |
| 56 | +-- By default, a path with a trailing separator is implicitly considered a |
| 57 | +-- directory path. However, the absence of a trailing separator does not |
| 58 | +-- indicate whether the path is a file or a directory — it could be either. |
| 59 | +-- Therefore, when using the @Path@ type, the append operation allows appending |
| 60 | +-- to paths even if they lack a trailing separator. However, when creating a |
| 61 | +-- typed path of type 'File', the conversion fails unless the trailing |
| 62 | +-- separator is explicitly removed. |
67 | 63 | -- |
68 | 64 | -- = Flexible Typing |
69 | 65 | -- |
70 | | --- You can use the 'Rooted', 'Branch' or 'Dir', 'File' types independent of |
71 | | --- each other by using only the required module. If you want both types of |
72 | | --- distinctions then you can use them together as well using the |
73 | | --- "Streamly.FileSystem.Path.SegNode" module. For example, the @Rooted (Dir |
74 | | --- Path)@ represents a rooted path which is a directory. You can only append to |
75 | | --- a path that has 'Dir' in it and you can only append a 'Branch' type. |
76 | | --- |
77 | | --- You can choose to use just the basic 'Path' type or any combination of safer |
78 | | --- types. You can upgrade or downgrade the safety by converting types using the |
79 | | --- @adapt@ operation. Whenever a less restrictive path type is converted to a |
80 | | --- more restrictive path type, the conversion involves run-time checks and it |
81 | | --- may fail. However, a more restrictive path type can be freely converted to a |
82 | | --- less restrictive one. |
| 66 | +-- You can use the 'Rooted', 'Branch', 'Dir', and 'File' types independently by |
| 67 | +-- importing only the required modules. If you want both types of distinctions, |
| 68 | +-- you can use them together via the "Streamly.FileSystem.Path.SegNode" module. |
| 69 | +-- For example, @Rooted (Dir Path)@ represents a rooted path that is a |
| 70 | +-- directory. You can append other paths only to paths that have a 'Dir' type, |
| 71 | +-- and only a path of type 'Branch' can be appended. |
| 72 | +-- |
| 73 | +-- You may choose to use the basic 'Path' type or any combination of the safer |
| 74 | +-- types. You can upgrade or downgrade the safety level by converting between |
| 75 | +-- types using the @adapt@ operation. When converting from a less restrictive |
| 76 | +-- type to a more restrictive one, run-time checks are performed, and the |
| 77 | +-- conversion may fail. However, converting from a more restrictive type to a |
| 78 | +-- less restrictive one is always allowed. |
83 | 79 | -- |
84 | 80 | -- = Extensibility |
85 | 81 | -- |
|
0 commit comments