@@ -527,28 +527,34 @@ defmodule Module.Types.Descr do
527527      { :term ,  [ ] ,  [ ] } 
528528    else 
529529      # Dynamic always come first for visibility 
530-       { dynamic ,  descr }  = 
530+       { dynamic ,  static }  = 
531531        case  :maps . take ( :dynamic ,  descr )  do 
532-           :error  ->  { [ ] ,  descr } 
533-           { :term ,  descr }  ->  { to_quoted ( :dynamic ,   : term,  opts ) ,   descr } 
534-           { dynamic ,  descr }  ->  { to_quoted ( :dynamic ,   difference ( dynamic ,  descr ) ,  opts ) ,   descr } 
532+           :error  ->  { % { } ,  descr } 
533+           { :term ,  static }  ->  { : term,  static } 
534+           { dynamic ,  static }  ->  { difference ( dynamic ,  static ) ,  static } 
535535        end 
536536
537+       { static ,  dynamic ,  extra }  =  fun_denormalize ( static ,  dynamic ,  opts ) 
538+ 
537539      # Merge empty list and list together if they both exist 
538-       { extra ,  descr }  = 
539-         case  descr  do 
540+       { extra ,  static }  = 
541+         case  static  do 
540542          % { list:  list ,  bitmap:  bitmap }  when  ( bitmap  &&&  @ bit_empty_list )  !=  0  -> 
541-             descr  =  descr  |>  Map . delete ( :list )  |>  Map . replace! ( :bitmap ,  bitmap  -  @ bit_empty_list ) 
542-             { list_to_quoted ( list ,  true ,  opts ) ,  descr } 
543+             static  = 
544+               static 
545+               |>  Map . delete ( :list ) 
546+               |>  Map . replace! ( :bitmap ,  bitmap  -  @ bit_empty_list ) 
547+ 
548+             { list_to_quoted ( list ,  true ,  opts )  ++  extra ,  static } 
543549
544550          % { }  -> 
545-             { [ ] ,   descr } 
551+             { extra ,   static } 
546552        end 
547553
548554      unions  = 
549-         dynamic  ++ 
555+         to_quoted ( : dynamic,   dynamic ,   opts )  ++ 
550556          Enum . sort ( 
551-             extra  ++  Enum . flat_map ( descr ,  fn  { key ,  value }  ->  to_quoted ( key ,  value ,  opts )  end ) 
557+             extra  ++  Enum . flat_map ( static ,  fn  { key ,  value }  ->  to_quoted ( key ,  value ,  opts )  end ) 
552558          ) 
553559
554560      case  unions  do 
@@ -564,7 +570,7 @@ defmodule Module.Types.Descr do
564570  defp  to_quoted ( :map ,  dnf ,  opts ) ,  do:  map_to_quoted ( dnf ,  opts ) 
565571  defp  to_quoted ( :list ,  dnf ,  opts ) ,  do:  list_to_quoted ( dnf ,  false ,  opts ) 
566572  defp  to_quoted ( :tuple ,  dnf ,  opts ) ,  do:  tuple_to_quoted ( dnf ,  opts ) 
567-   defp  to_quoted ( :fun ,  dnf ,  opts ) ,  do:  fun_to_quoted ( dnf ,  opts ) 
573+   defp  to_quoted ( :fun ,  bdd ,  opts ) ,  do:  fun_to_quoted ( bdd ,  opts ) 
568574
569575  @ doc  """ 
570576  Converts a descr to its quoted string representation. 
@@ -1488,21 +1494,139 @@ defmodule Module.Types.Descr do
14881494    end 
14891495  end 
14901496
1491-   # Converts a function BDD (Binary Decision Diagram) to its quoted representation. 
1492-   defp  fun_to_quoted ( bdd ,  opts )  do 
1493-     arrows  =  fun_get ( bdd ) 
1497+   # Converts the static and dynamic parts of descr to its quoted 
1498+   # representation. The goal here is to the opposite of fun_descr 
1499+   # and put static and dynamic parts back together to improve 
1500+   # pretty printing. 
1501+   defp  fun_denormalize ( % { fun:  static_bdd }  =  static ,  % { fun:  dynamic_bdd }  =  dynamic ,  opts )  do 
1502+     static_pos  =  fun_get_pos ( static_bdd ) 
1503+     dynamic_pos  =  fun_get_pos ( dynamic_bdd ) 
1504+ 
1505+     if  static_pos  !=  [ ]  and  dynamic_pos  !=  [ ]  do 
1506+       { dynamic_pos ,  static_pos }  =  fun_denormalize_pos ( dynamic_pos ,  static_pos ) 
14941507
1495-     for  { positives ,  negatives }  <-  arrows ,  not  fun_empty? ( positives ,  negatives )  do 
1496-       fun_intersection_to_quoted ( positives ,  opts ) 
1508+       quoted  = 
1509+         if  dynamic_pos  ==  [ ]  do 
1510+           fun_pos_to_quoted ( static_pos ,  opts ) 
1511+         else 
1512+           { :or ,  [ ] , 
1513+            [ 
1514+              { :dynamic ,  [ ] ,  [ fun_pos_to_quoted ( dynamic_pos ,  opts ) ] } , 
1515+              fun_pos_to_quoted ( static_pos ,  opts ) 
1516+            ] } 
1517+         end 
1518+ 
1519+       { Map . delete ( static ,  :fun ) ,  Map . delete ( dynamic ,  :fun ) ,  [ quoted ] } 
1520+     else 
1521+       { static ,  dynamic ,  [ ] } 
14971522    end 
1498-     |>  case  do 
1523+   end 
1524+ 
1525+   defp  fun_denormalize ( static ,  dynamic ,  _opts )  do 
1526+     { static ,  dynamic ,  [ ] } 
1527+   end 
1528+ 
1529+   defp  fun_denormalize_pos ( dynamic_unions ,  static_unions )  do 
1530+     Enum . reduce ( dynamic_unions ,  { [ ] ,  static_unions } ,  fn 
1531+       # Handle fun() types accordingly 
1532+       [ ] ,  { dynamic_unions ,  static_unions }  -> 
1533+         { [ [ ]  |  dynamic_unions ] ,  static_unions } 
1534+ 
1535+       dynamic_intersections ,  { dynamic_unions ,  static_unions }  -> 
1536+         { dynamic_intersections ,  static_unions }  = 
1537+           Enum . reduce ( dynamic_intersections ,  { [ ] ,  static_unions } ,  fn 
1538+             { args ,  return } ,  { acc ,  static_unions }  -> 
1539+               case  fun_denormalize_arrow ( args ,  return ,  static_unions )  do 
1540+                 { :ok ,  static_unions }  ->  { acc ,  static_unions } 
1541+                 :error  ->  { [ { args ,  return }  |  acc ] ,  static_unions } 
1542+               end 
1543+           end ) 
1544+ 
1545+         if  dynamic_intersections  ==  [ ]  do 
1546+           { dynamic_unions ,  static_unions } 
1547+         else 
1548+           { [ dynamic_intersections  |  dynamic_unions ] ,  static_unions } 
1549+         end 
1550+     end ) 
1551+   end 
1552+ 
1553+   defp  fun_denormalize_arrow ( dynamic_args ,  dynamic_return ,  static_unions )  do 
1554+     pivot ( static_unions ,  [ ] ,  fn  static_intersections  -> 
1555+       pivot ( static_intersections ,  [ ] ,  fn  { static_args ,  static_return }  -> 
1556+         if  subtype? ( static_return ,  dynamic_return )  and  args_subtype? ( dynamic_args ,  static_args )  do 
1557+           args  = 
1558+             Enum . zip_with ( static_args ,  dynamic_args ,  fn  static_arg ,  dynamic_arg  -> 
1559+               union ( dynamic ( difference ( static_arg ,  dynamic_arg ) ) ,  dynamic_arg ) 
1560+             end ) 
1561+ 
1562+           return  =  union ( dynamic ( difference ( dynamic_return ,  static_return ) ) ,  static_return ) 
1563+           { :ok ,  { args ,  return } } 
1564+         else 
1565+           :error 
1566+         end 
1567+       end ) 
1568+     end ) 
1569+   end 
1570+ 
1571+   defp  arrow_subtype? ( left_args ,  left_return ,  right_args ,  right_return )  do 
1572+     subtype? ( right_return ,  left_return )  and  args_subtype? ( left_args ,  right_args ) 
1573+   end 
1574+ 
1575+   defp  args_subtype? ( left_args ,  right_args )  do 
1576+     Enum . zip_reduce ( left_args ,  right_args ,  true ,  fn  left ,  right ,  acc  -> 
1577+       acc  and  subtype? ( left ,  right ) 
1578+     end ) 
1579+   end 
1580+ 
1581+   defp  pivot ( [ head  |  tail ] ,  acc ,  fun )  do 
1582+     case  fun . ( head )  do 
1583+       { :ok ,  value }  ->  { :ok ,  acc  ++  [ value  |  tail ] } 
1584+       :error  ->  pivot ( tail ,  [ head  |  acc ] ,  fun ) 
1585+     end 
1586+   end 
1587+ 
1588+   defp  pivot ( [ ] ,  _acc ,  _fun ) ,  do:  :error 
1589+ 
1590+   # Converts a function BDD (Binary Decision Diagram) to its quoted representation 
1591+   defp  fun_to_quoted ( bdd ,  opts )  do 
1592+     case  fun_get_pos ( bdd )  do 
14991593      [ ]  ->  [ ] 
1500-       multiple  ->  [ Enum . reduce ( multiple ,   & { :or ,   [ ] ,   [ & 2 ,   & 1 ] } ) ] 
1594+       pos  ->  [ fun_pos_to_quoted ( pos ,   opts ) ] 
15011595    end 
15021596  end 
15031597
1598+   defp  fun_get_pos ( bdd )  do 
1599+     for  { pos ,  negs }  <-  fun_get ( bdd ) ,  not  fun_empty? ( pos ,  negs )  do 
1600+       fun_filter_subset ( pos ,  [ ] ) 
1601+     end 
1602+   end 
1603+ 
1604+   defp  fun_filter_subset ( [ ] ,  acc ) ,  do:  acc 
1605+ 
1606+   defp  fun_filter_subset ( [ { args ,  return }  |  tail ] ,  acc )  do 
1607+     # If another arrow is a subset of the current one, we skip it 
1608+     if  Enum . any? ( tail ,  fn  { other_args ,  other_return }  -> 
1609+          arrow_subtype? ( other_args ,  other_return ,  args ,  return ) 
1610+        end )  or 
1611+          Enum . any? ( acc ,  fn  { other_args ,  other_return }  -> 
1612+            arrow_subtype? ( other_args ,  other_return ,  args ,  return ) 
1613+          end )  do 
1614+       fun_filter_subset ( tail ,  acc ) 
1615+     else 
1616+       fun_filter_subset ( tail ,  [ { args ,  return }  |  acc ] ) 
1617+     end 
1618+   end 
1619+ 
1620+   defp  fun_pos_to_quoted ( [ _  |  _ ]  =  pos ,  opts )  do 
1621+     pos 
1622+     |>  Enum . sort ( ) 
1623+     |>  Enum . map ( & fun_intersection_to_quoted ( & 1 ,  opts ) ) 
1624+     |>  Enum . reduce ( & { :or ,  [ ] ,  [ & 2 ,  & 1 ] } ) 
1625+   end 
1626+ 
15041627  defp  fun_intersection_to_quoted ( intersection ,  opts )  do 
15051628    intersection 
1629+     |>  Enum . sort ( ) 
15061630    |>  Enum . map ( fn  { args ,  ret }  -> 
15071631      { :__block__ ,  [ ] , 
15081632       [ [ { :-> ,  [ ] ,  [ Enum . map ( args ,  & to_quoted ( & 1 ,  opts ) ) ,  to_quoted ( ret ,  opts ) ] } ] ] } 
@@ -1904,6 +2028,9 @@ defmodule Module.Types.Descr do
19042028
19052029  defp  dynamic_to_quoted ( descr ,  opts )  do 
19062030    cond  do 
2031+       descr  ==  % { }  -> 
2032+         [ ] 
2033+ 
19072034      term_type? ( descr )  -> 
19082035        [ { :dynamic ,  [ ] ,  [ ] } ] 
19092036
0 commit comments