| | 171 | {-| |
| | 172 | Count the total number of types in a class tree, including both internal and |
| | 173 | leaf nodes. |
| | 174 | |
| | 175 | This is used by 'deltaType' to ensure that incompatible types are always |
| | 176 | further apart than compatible types. |
| | 177 | -} |
| | 178 | countTree :: ClassTree -> Int |
| | 179 | countTree (Node _ []) = 1 |
| | 180 | countTree (Node _ cs) = 1 + sum (map countTree cs) |
| | 181 | |
| | 182 | {-| |
| | 183 | Find the \'difference\' between two types in the given class tree (for MMD |
| | 184 | purposes and such). |
| | 185 | |
| | 186 | Identical types (that exist in the class tree) produce 0. Compatible types |
| | 187 | will produce a small positive number representing their distance. |
| | 188 | Incompatible will produce a distance larger |
| | 189 | than any two compatible types. If one (or both) of the types doesn't exist in |
| | 190 | the tree, the result is a very large number. |
| | 191 | |
| | 192 | > <scook0> is deltaType supposed to be returning large positive numbers for |
| | 193 | > types that are actually incompatible? |
| | 194 | > <autrijus> that is a open design question. |
| | 195 | > <autrijus> it is that way because we want |
| | 196 | > <autrijus> '1'+'2' |
| | 197 | > <autrijus> to work |
| | 198 | > <scook0> I see |
| | 199 | > <autrijus> without having to define <+> as Scalar Scalar |
| | 200 | > <autrijus> I think I did think of leaving a compatibleTypes as remedy |
| | 201 | > <autrijus> to specify things that are fundamentally uncastable |
| | 202 | > <scook0> I think I'll just document the current behaviour for now |
| | 203 | > <autrijus> nod. it is a mess. it really wants a rewrite. |
| | 204 | -} |
| | 205 | deltaType :: ClassTree -- ^ Class tree to use for the comparison |
| | 206 | -> Type -- ^ Base type |
| | 207 | -> Type -- ^ Possibly-derived type |
| | 208 | -> Int |
| | 209 | deltaType = junctivate min max $ \tree base target -> |
| | 210 | let distance = distanceType tree base target in |
| | 211 | if distance < 0 |
| | 212 | then countTree tree - distance |
| | 213 | else distance |
| | 214 | |
| | 215 | {-| |
| | 216 | Autothreading of comparisons between junctive types. |
| | 217 | |
| | 218 | Just as autothreading over value junctions will perform an operation on all |
| | 219 | junction elements and combine the results back into a junction, this function |
| | 220 | autothreads some type comparison over all the possible type permutations, |
| | 221 | then combines the results using two user-specified /functions/. |
| | 222 | |
| | 223 | E.g. if we want to check whether the type @(Int|Str)@ is a @Num@, we first |
| | 224 | check whether @Int@ is a @Num@ (@True@), then check whether @Str@ is a num |
| | 225 | (@False@), then combine the results using the specified disjunctive combiner |
| | 226 | (in this case Haskell's @(||)@). The result is thus @True@. |
| | 227 | -} |
| | 228 | junctivate :: (t -> t -> t) -- ^ Function to combine results over disjunctive |
| | 229 | -- (@|@) types |
| | 230 | -> (t -> t -> t) -- ^ Function to combine results over conjunctive |
| | 231 | -- (@\&@) types |
| | 232 | -> (ClassTree -> Type -> Type -> t) |
| | 233 | -- ^ Function that will actually perform the |
| | 234 | -- comparison (on non-junctive types) |
| | 235 | -> ClassTree -- ^ Class tree to pass to the comparison function |
| | 236 | -> Type -- ^ First type to compare |
| | 237 | -> Type -- ^ Second type to compare |
| | 238 | -> t |
| | 239 | junctivate or and f tree base target |
| | 240 | | TypeOr t1 t2 <- target |
| | 241 | = redo base t1 `or` redo base t2 |
| | 242 | | TypeOr b1 b2 <- base |
| | 243 | = redo b1 target `or` redo b2 target |
| | 244 | | TypeAnd t1 t2 <- target |
| | 245 | = redo base t1 `and` redo base t2 |
| | 246 | | TypeAnd b1 b2 <- base |
| | 247 | = redo b1 target `and` redo b2 target |
| | 248 | | otherwise |
| | 249 | = f tree base target |
| | 250 | where |
| | 251 | redo = junctivate or and f tree |
| | 252 | |
| | 253 | -- When saying Int.isa(Scalar), Scalar is the base, Int is the target |
| | 254 | {-| |
| | 255 | A more convenient version of 'isaType\'' that automatically converts the base |
| | 256 | type string into an actual 'Type' value. |
| | 257 | -} |
| | 258 | isaType :: ClassTree -- ^ Class tree to use for the comparison |
| | 259 | -> String -- ^ Base type |
| | 260 | -> Type -- ^ Possibly-derived type |
| | 261 | -> Bool |
| | 262 | isaType tree base target = isaType' tree (mkType base) target |
| | 263 | |
| | 264 | {-| |
| | 265 | Return true if the second type (the \'target\') is derived-from or equal-to the |
| | 266 | first type (the \'base\'), in the context of the given class tree. |
| | 267 | |
| | 268 | This function will autothread over junctive types. |
| | 269 | -} |
| | 270 | isaType' :: ClassTree -- ^ Class tree to use for the comparison |
| | 271 | -> Type -- ^ Base type |
| | 272 | -> Type -- ^ Possibly-derived type |
| | 273 | -> Bool |
| | 274 | isaType' = junctivate (||) (&&) $ \tree base target -> |
| | 275 | distanceType tree base target >= 0 |
| | 276 | |
| | 277 | {-| |
| | 278 | Compute the \'distance\' between two types by applying 'findList' to each of |
| | 279 | them, and passing the resulting type chains to 'compareList'. |
| | 280 | |
| | 281 | See 'compareList' for further details. |
| | 282 | -} |
| | 283 | distanceType :: ClassTree -> Type -> Type -> Int |
| | 284 | distanceType tree base target = compareList l1 l2 |
| | 285 | -- | not (castOk base target) = 0 |
| | 286 | -- | otherwise = compareList l1 l2 |
| | 287 | where |
| | 288 | l1 = findList base tree |
| | 289 | l2 = findList target tree |
| | 290 | |
| | 291 | {- |
| | 292 | -- | (This is currently unused...) |
| | 293 | castOk :: a -> b -> Bool |
| | 294 | castOk _ _ = True |
| | 295 | -} |
| | 296 | |
| | 297 | {-| |
| | 298 | Take two inheritance chains produced by 'findList', and determine how |
| | 299 | \'compatible\' the first one is with the second. |
| | 300 | |
| | 301 | Compatible types will produce a number indicating how distant they are. |
| | 302 | Incompatible types produce a negative number indicating how much the base type |
| | 303 | would need to be relaxed. If one (or both) types doesn't exist in the tree, a |
| | 304 | large negative number is produced |
| | 305 | |
| | 306 | E.g.: |
| | 307 | |
| | 308 | * comparing @Int@ and @Int@ will produce 0 |
| | 309 | |
| | 310 | * comparing @Scalar@ and @String@ will produce 1 |
| | 311 | |
| | 312 | * comparing @Num@ and @Scalar@ will produce -2 |
| | 313 | |
| | 314 | * comparing @Blorple@ and @Method@ will produce -999 (or similar) |
| | 315 | -} |
| | 316 | compareList :: [Type] -- ^ Base type's chain |
| | 317 | -> [Type] -- ^ Possibly-derived type's chain |
| | 318 | -> Int |
| | 319 | compareList [] _ = -999 -- XXX hack (nonexistent base type?) |
| | 320 | compareList _ [] = -999 -- XXX hack (incompatible types) |
| | 321 | compareList l1 l2 |
| | 322 | | last l1 `elem` l2 = length(l2 \\ l1) -- compatible types |
| | 323 | | last l2 `elem` l1 = - length(l1 \\ l2) -- anti-compatible types |
| | 324 | | otherwise = compareList l1 (init l2) |
| | 325 | |
| | 326 | {-| |
| | 327 | Produce the type \'inheritance\' chain leading from the base type (@Any@) to |
| | 328 | the given type. |
| | 329 | |
| | 330 | e.g. |
| | 331 | |
| | 332 | @ |
| | 333 | 'findList' ('MkType' \"Num\") 'initTree' |
| | 334 | @ |
| | 335 | |
| | 336 | will produce the list of types |
| | 337 | |
| | 338 | @ |
| | 339 | Any, Void, Object, Scalar, Complex, Num |
| | 340 | @ |
| | 341 | |
| | 342 | This function does /not/ expect to be given junctive types. |
| | 343 | -} |
| | 344 | findList :: Type -- ^ 'Type' to find the inheritance chain of |
| | 345 | -> ClassTree -- ^ Class tree to look in |
| | 346 | -> [Type] |
| | 347 | findList base (Node l cs) |
| | 348 | | base == l = [l] |
| | 349 | | Just ls <- find (not . null) found = l:ls |
| | 350 | | otherwise = [] |
| | 351 | where |
| | 352 | found :: [[Type]] |
| | 353 | found = map (findList base) cs |
| | 354 | |
| | 355 | {- |
| | 356 | {-| |
| | 357 | Pretty-print the initial class tree, using @Tree@'s @drawTree@. |
| | 358 | |
| | 359 | (This seems to be a debugging aid, since it's not actually used anywhere.) |
| | 360 | -} |
| | 361 | prettyTypes :: String |
| | 362 | prettyTypes = drawTree $ fmap show initTree |
| | 363 | -} |
| | 364 | |
| | 365 | {-| |
| | 366 | Add a new \'top-level\' type to the class tree, under @Object@. |
| | 367 | -} |
| | 368 | addNode :: ClassTree -> Type -> ClassTree |
| | 369 | addNode (Node obj [Node any (Node item ns:rest), junc]) typ = |
| | 370 | Node obj [Node any (Node item ((Node typ []):ns):rest), junc] |
| | 371 | addNode _ _ = error "malformed tree" |
| | 372 | |
| | 373 | {-| |
| | 374 | Default class tree, containing all built-in types. |
| | 375 | -} |
| | 376 | initTree :: ClassTree |
| | 377 | initTree = fmap MkType $ Node "Object" |
| | 378 | [ Node "Any" |
| | 379 | [ Node "Item" |
| | 380 | [ Node "List" |
| | 381 | [ Node "Lazy" |
| | 382 | [ Node "Array" |
| | 383 | [ Node "Array::Const" [] |
| | 384 | , Node "Array::Slice" [] |
| | 385 | ] |
| | 386 | , Node "Hash" |
| | 387 | [ Node "Hash::Const" [] |
| | 388 | , Node "Hash::Env" [] |
| | 389 | ] |
| | 390 | ] |
| | 391 | , Node "Eager" [] |
| | 392 | ] |
| | 393 | , Node "Scalar" |
| | 394 | [ Node "Complex" |
| | 395 | [ Node "Num" |
| | 396 | [ Node "Rat" |
| | 397 | [ Node "Int" |
| | 398 | [ Node "Bit" [] ] ] ] ] |
| | 399 | , Node "Bool" [] |
| | 400 | , Node "Str" [] |
| | 401 | , Node "Ref" [] |
| | 402 | , Node "IO" |
| | 403 | [ Node "IO::Dir" [] |
| | 404 | ] |
| | 405 | , Node "Socket" [] |
| | 406 | , Node "Thread" [] |
| | 407 | , Node "Code" |
| | 408 | [ Node "Routine" |
| | 409 | [ Node "Sub" |
| | 410 | [ Node "Method" [] |
| | 411 | , Node "Submethod" [] -- why isn't this a node off Method? - mugwump |
| | 412 | ] |
| | 413 | , Node "Macro" [] ] |
| | 414 | , Node "Block" [] |
| | 415 | ] |
| | 416 | , Node "Rul" [] |
| | 417 | , Node "Pugs::Internals::VRule" [] |
| | 418 | , Node "Match" [] |
| | 419 | , Node "Scalar::Const" [] |
| | 420 | , Node "Scalar::Proxy" [] |
| | 421 | , Node "Scalar::Lazy" [] |
| | 422 | , Node "Scalar::Perl5" [] |
| | 423 | , Node "Proxy" [] |
| | 424 | , Node "Control::Caller" [] |
| | 425 | , Node "Time::Local" [] |
| | 426 | , Node "Type" |
| | 427 | [ Node "Package" |
| | 428 | [ Node "Module" |
| | 429 | [ Node "Class" |
| | 430 | [ Node "Role" [] |
| | 431 | , Node "Grammar" [] |
| | 432 | ] ] ] ] |
| | 433 | ] |
| | 434 | ] |
| | 435 | , Node "Pair" [] |
| | 436 | ] |
| | 437 | , Node "Junction" [] ] |
| | 438 | |