F-bounded types and methods with type parameters at argument and return sites












2















I have an F-bounded type, and my objective is to create a type-parametrized method in order to be able to reuse it. Here is the example code:



trait FType {
type ThisType <: FType

def deepCopy(): ThisType

}

class ConcreteType extends FType {

override type ThisType = ConcreteType

override def deepCopy(): ConcreteType = this

}

class ConcreteType2 extends FType {

override type ThisType = ConcreteType2

override def deepCopy(): ConcreteType2 = this

}

object FType {

def deepCopy[T <: FType](_type: T): T = {
_type.deepCopy()
}

/* def deepCopy2(c: ConcreteType2): ConcreteType2 = {
c.deepCopy()
}*/

def main(args: Array[String]): Unit = {
//deepCopy2(new ConcreteType2)
}

}


However, the code does not compile. The compiler throws this error:



Error:(29, 19) type mismatch;
found : _type.ThisType
required: T
_type.deepCopy()


I understand that it has something to to with path-dependent types since _type.ThisType is not the same type as T.



But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type? If the types are not exactly the same, how is that deepCopy2 compiles?



Note: I know I can avoid using type parameters in deepCopy by using method overloading for each of the concrete types.










share|improve this question





























    2















    I have an F-bounded type, and my objective is to create a type-parametrized method in order to be able to reuse it. Here is the example code:



    trait FType {
    type ThisType <: FType

    def deepCopy(): ThisType

    }

    class ConcreteType extends FType {

    override type ThisType = ConcreteType

    override def deepCopy(): ConcreteType = this

    }

    class ConcreteType2 extends FType {

    override type ThisType = ConcreteType2

    override def deepCopy(): ConcreteType2 = this

    }

    object FType {

    def deepCopy[T <: FType](_type: T): T = {
    _type.deepCopy()
    }

    /* def deepCopy2(c: ConcreteType2): ConcreteType2 = {
    c.deepCopy()
    }*/

    def main(args: Array[String]): Unit = {
    //deepCopy2(new ConcreteType2)
    }

    }


    However, the code does not compile. The compiler throws this error:



    Error:(29, 19) type mismatch;
    found : _type.ThisType
    required: T
    _type.deepCopy()


    I understand that it has something to to with path-dependent types since _type.ThisType is not the same type as T.



    But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type? If the types are not exactly the same, how is that deepCopy2 compiles?



    Note: I know I can avoid using type parameters in deepCopy by using method overloading for each of the concrete types.










    share|improve this question



























      2












      2








      2


      1






      I have an F-bounded type, and my objective is to create a type-parametrized method in order to be able to reuse it. Here is the example code:



      trait FType {
      type ThisType <: FType

      def deepCopy(): ThisType

      }

      class ConcreteType extends FType {

      override type ThisType = ConcreteType

      override def deepCopy(): ConcreteType = this

      }

      class ConcreteType2 extends FType {

      override type ThisType = ConcreteType2

      override def deepCopy(): ConcreteType2 = this

      }

      object FType {

      def deepCopy[T <: FType](_type: T): T = {
      _type.deepCopy()
      }

      /* def deepCopy2(c: ConcreteType2): ConcreteType2 = {
      c.deepCopy()
      }*/

      def main(args: Array[String]): Unit = {
      //deepCopy2(new ConcreteType2)
      }

      }


      However, the code does not compile. The compiler throws this error:



      Error:(29, 19) type mismatch;
      found : _type.ThisType
      required: T
      _type.deepCopy()


      I understand that it has something to to with path-dependent types since _type.ThisType is not the same type as T.



      But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type? If the types are not exactly the same, how is that deepCopy2 compiles?



      Note: I know I can avoid using type parameters in deepCopy by using method overloading for each of the concrete types.










      share|improve this question
















      I have an F-bounded type, and my objective is to create a type-parametrized method in order to be able to reuse it. Here is the example code:



      trait FType {
      type ThisType <: FType

      def deepCopy(): ThisType

      }

      class ConcreteType extends FType {

      override type ThisType = ConcreteType

      override def deepCopy(): ConcreteType = this

      }

      class ConcreteType2 extends FType {

      override type ThisType = ConcreteType2

      override def deepCopy(): ConcreteType2 = this

      }

      object FType {

      def deepCopy[T <: FType](_type: T): T = {
      _type.deepCopy()
      }

      /* def deepCopy2(c: ConcreteType2): ConcreteType2 = {
      c.deepCopy()
      }*/

      def main(args: Array[String]): Unit = {
      //deepCopy2(new ConcreteType2)
      }

      }


      However, the code does not compile. The compiler throws this error:



      Error:(29, 19) type mismatch;
      found : _type.ThisType
      required: T
      _type.deepCopy()


      I understand that it has something to to with path-dependent types since _type.ThisType is not the same type as T.



      But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type? If the types are not exactly the same, how is that deepCopy2 compiles?



      Note: I know I can avoid using type parameters in deepCopy by using method overloading for each of the concrete types.







      scala path-dependent-type f-bounded-polymorphism






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 26 '18 at 12:41









      Alonso Dominguez

      6,34311936




      6,34311936










      asked Nov 26 '18 at 12:18









      vicabavicaba

      1,17911528




      1,17911528
























          2 Answers
          2






          active

          oldest

          votes


















          5















          If the types are not exactly the same, how is that deepCopy2 compiles?




          That is quite simple. It works because there is no polymorphism involved. It's statically known that ConcreteType2 has deepCopy() method that returns ConcreteType2. You could even remove type ThisType from whole hierarchy and it would still work the same way.






          But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type?




          You need to tell the compiler that it's the same, as you haven't specified enough. Let's take a look at an example that works and is polymorphic:



          def deepCopy[A <: FType { type ThisType = A }](_type: A): A = _type.deepCopy()
          // ^ --important bit-- ^


          This defines a method that works for any A that is FType and also it's type member ThisType is set to A, tying them together. That means it would work for your definitions of ConcreteType and ConcreteType2. It would NOT compile, however, for classes that aren't defined properly, such as this one:



          class Bogus extends FType {
          override type ThisType = ConcreteType2
          override def deepCopy(): ConcreteType2 = new ConcreteType2
          }

          deepCopy(new Bogus)




          Alternatively, let's start with a slightly modified version of your method:



          def deepCopyPD[A <: FType](_type: A): _type.ThisType = _type.deepCopy()
          ^path-dependent^


          It puts no constraints on ThisType, but in fact compiler would be able to infer proper versions of it for all cases:



          val x: ConcreteType2 = deepCopyPD(new ConcreteType2)
          val y: ConcreteType2 = deepCopyPD(new Bogus) // yep, this invocation is possible with such signature


          However, it is also possible to further add constraints using type equality evidence as implicit parameter:



          def deepCopyPD2[A <: FType](_type: A)(implicit ev: _type.ThisType =:= A): A = _type.deepCopy()


          This, once more, forbids the invocation with Bogus






          share|improve this answer































            2














            In F-bounded types, you'd normally have ThisType as a type parameter instead of a type member:



            trait FType[ThisType <: FType] {
            def deepCopy(): ThisType
            }

            class ConcreteType extends FType[ConcreteType] {
            override def deepCopy(): ConcreteType = this
            }

            // in object FType
            def deepCopy[T <: FType[T]](_type: T): T = {
            _type.deepCopy()
            }


            Note the difference: in FType.deepCopy the compiler knows that the return type of _type.deepCopy() is T.



            You can do the same with type members:



            def deepCopy[T <: FType { type ThisType <: T }](_type: T): T = 
            _type.deepCopy()





            share|improve this answer
























              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "1"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53480975%2ff-bounded-types-and-methods-with-type-parameters-at-argument-and-return-sites%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              5















              If the types are not exactly the same, how is that deepCopy2 compiles?




              That is quite simple. It works because there is no polymorphism involved. It's statically known that ConcreteType2 has deepCopy() method that returns ConcreteType2. You could even remove type ThisType from whole hierarchy and it would still work the same way.






              But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type?




              You need to tell the compiler that it's the same, as you haven't specified enough. Let's take a look at an example that works and is polymorphic:



              def deepCopy[A <: FType { type ThisType = A }](_type: A): A = _type.deepCopy()
              // ^ --important bit-- ^


              This defines a method that works for any A that is FType and also it's type member ThisType is set to A, tying them together. That means it would work for your definitions of ConcreteType and ConcreteType2. It would NOT compile, however, for classes that aren't defined properly, such as this one:



              class Bogus extends FType {
              override type ThisType = ConcreteType2
              override def deepCopy(): ConcreteType2 = new ConcreteType2
              }

              deepCopy(new Bogus)




              Alternatively, let's start with a slightly modified version of your method:



              def deepCopyPD[A <: FType](_type: A): _type.ThisType = _type.deepCopy()
              ^path-dependent^


              It puts no constraints on ThisType, but in fact compiler would be able to infer proper versions of it for all cases:



              val x: ConcreteType2 = deepCopyPD(new ConcreteType2)
              val y: ConcreteType2 = deepCopyPD(new Bogus) // yep, this invocation is possible with such signature


              However, it is also possible to further add constraints using type equality evidence as implicit parameter:



              def deepCopyPD2[A <: FType](_type: A)(implicit ev: _type.ThisType =:= A): A = _type.deepCopy()


              This, once more, forbids the invocation with Bogus






              share|improve this answer




























                5















                If the types are not exactly the same, how is that deepCopy2 compiles?




                That is quite simple. It works because there is no polymorphism involved. It's statically known that ConcreteType2 has deepCopy() method that returns ConcreteType2. You could even remove type ThisType from whole hierarchy and it would still work the same way.






                But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type?




                You need to tell the compiler that it's the same, as you haven't specified enough. Let's take a look at an example that works and is polymorphic:



                def deepCopy[A <: FType { type ThisType = A }](_type: A): A = _type.deepCopy()
                // ^ --important bit-- ^


                This defines a method that works for any A that is FType and also it's type member ThisType is set to A, tying them together. That means it would work for your definitions of ConcreteType and ConcreteType2. It would NOT compile, however, for classes that aren't defined properly, such as this one:



                class Bogus extends FType {
                override type ThisType = ConcreteType2
                override def deepCopy(): ConcreteType2 = new ConcreteType2
                }

                deepCopy(new Bogus)




                Alternatively, let's start with a slightly modified version of your method:



                def deepCopyPD[A <: FType](_type: A): _type.ThisType = _type.deepCopy()
                ^path-dependent^


                It puts no constraints on ThisType, but in fact compiler would be able to infer proper versions of it for all cases:



                val x: ConcreteType2 = deepCopyPD(new ConcreteType2)
                val y: ConcreteType2 = deepCopyPD(new Bogus) // yep, this invocation is possible with such signature


                However, it is also possible to further add constraints using type equality evidence as implicit parameter:



                def deepCopyPD2[A <: FType](_type: A)(implicit ev: _type.ThisType =:= A): A = _type.deepCopy()


                This, once more, forbids the invocation with Bogus






                share|improve this answer


























                  5












                  5








                  5








                  If the types are not exactly the same, how is that deepCopy2 compiles?




                  That is quite simple. It works because there is no polymorphism involved. It's statically known that ConcreteType2 has deepCopy() method that returns ConcreteType2. You could even remove type ThisType from whole hierarchy and it would still work the same way.






                  But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type?




                  You need to tell the compiler that it's the same, as you haven't specified enough. Let's take a look at an example that works and is polymorphic:



                  def deepCopy[A <: FType { type ThisType = A }](_type: A): A = _type.deepCopy()
                  // ^ --important bit-- ^


                  This defines a method that works for any A that is FType and also it's type member ThisType is set to A, tying them together. That means it would work for your definitions of ConcreteType and ConcreteType2. It would NOT compile, however, for classes that aren't defined properly, such as this one:



                  class Bogus extends FType {
                  override type ThisType = ConcreteType2
                  override def deepCopy(): ConcreteType2 = new ConcreteType2
                  }

                  deepCopy(new Bogus)




                  Alternatively, let's start with a slightly modified version of your method:



                  def deepCopyPD[A <: FType](_type: A): _type.ThisType = _type.deepCopy()
                  ^path-dependent^


                  It puts no constraints on ThisType, but in fact compiler would be able to infer proper versions of it for all cases:



                  val x: ConcreteType2 = deepCopyPD(new ConcreteType2)
                  val y: ConcreteType2 = deepCopyPD(new Bogus) // yep, this invocation is possible with such signature


                  However, it is also possible to further add constraints using type equality evidence as implicit parameter:



                  def deepCopyPD2[A <: FType](_type: A)(implicit ev: _type.ThisType =:= A): A = _type.deepCopy()


                  This, once more, forbids the invocation with Bogus






                  share|improve this answer














                  If the types are not exactly the same, how is that deepCopy2 compiles?




                  That is quite simple. It works because there is no polymorphism involved. It's statically known that ConcreteType2 has deepCopy() method that returns ConcreteType2. You could even remove type ThisType from whole hierarchy and it would still work the same way.






                  But how, then, can I take advantage of F-bounded types if the F-bounded type is not exactly the same as the type using the F-bounded type?




                  You need to tell the compiler that it's the same, as you haven't specified enough. Let's take a look at an example that works and is polymorphic:



                  def deepCopy[A <: FType { type ThisType = A }](_type: A): A = _type.deepCopy()
                  // ^ --important bit-- ^


                  This defines a method that works for any A that is FType and also it's type member ThisType is set to A, tying them together. That means it would work for your definitions of ConcreteType and ConcreteType2. It would NOT compile, however, for classes that aren't defined properly, such as this one:



                  class Bogus extends FType {
                  override type ThisType = ConcreteType2
                  override def deepCopy(): ConcreteType2 = new ConcreteType2
                  }

                  deepCopy(new Bogus)




                  Alternatively, let's start with a slightly modified version of your method:



                  def deepCopyPD[A <: FType](_type: A): _type.ThisType = _type.deepCopy()
                  ^path-dependent^


                  It puts no constraints on ThisType, but in fact compiler would be able to infer proper versions of it for all cases:



                  val x: ConcreteType2 = deepCopyPD(new ConcreteType2)
                  val y: ConcreteType2 = deepCopyPD(new Bogus) // yep, this invocation is possible with such signature


                  However, it is also possible to further add constraints using type equality evidence as implicit parameter:



                  def deepCopyPD2[A <: FType](_type: A)(implicit ev: _type.ThisType =:= A): A = _type.deepCopy()


                  This, once more, forbids the invocation with Bogus







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 26 '18 at 12:50









                  Oleg PyzhcovOleg Pyzhcov

                  4,6201821




                  4,6201821

























                      2














                      In F-bounded types, you'd normally have ThisType as a type parameter instead of a type member:



                      trait FType[ThisType <: FType] {
                      def deepCopy(): ThisType
                      }

                      class ConcreteType extends FType[ConcreteType] {
                      override def deepCopy(): ConcreteType = this
                      }

                      // in object FType
                      def deepCopy[T <: FType[T]](_type: T): T = {
                      _type.deepCopy()
                      }


                      Note the difference: in FType.deepCopy the compiler knows that the return type of _type.deepCopy() is T.



                      You can do the same with type members:



                      def deepCopy[T <: FType { type ThisType <: T }](_type: T): T = 
                      _type.deepCopy()





                      share|improve this answer




























                        2














                        In F-bounded types, you'd normally have ThisType as a type parameter instead of a type member:



                        trait FType[ThisType <: FType] {
                        def deepCopy(): ThisType
                        }

                        class ConcreteType extends FType[ConcreteType] {
                        override def deepCopy(): ConcreteType = this
                        }

                        // in object FType
                        def deepCopy[T <: FType[T]](_type: T): T = {
                        _type.deepCopy()
                        }


                        Note the difference: in FType.deepCopy the compiler knows that the return type of _type.deepCopy() is T.



                        You can do the same with type members:



                        def deepCopy[T <: FType { type ThisType <: T }](_type: T): T = 
                        _type.deepCopy()





                        share|improve this answer


























                          2












                          2








                          2







                          In F-bounded types, you'd normally have ThisType as a type parameter instead of a type member:



                          trait FType[ThisType <: FType] {
                          def deepCopy(): ThisType
                          }

                          class ConcreteType extends FType[ConcreteType] {
                          override def deepCopy(): ConcreteType = this
                          }

                          // in object FType
                          def deepCopy[T <: FType[T]](_type: T): T = {
                          _type.deepCopy()
                          }


                          Note the difference: in FType.deepCopy the compiler knows that the return type of _type.deepCopy() is T.



                          You can do the same with type members:



                          def deepCopy[T <: FType { type ThisType <: T }](_type: T): T = 
                          _type.deepCopy()





                          share|improve this answer













                          In F-bounded types, you'd normally have ThisType as a type parameter instead of a type member:



                          trait FType[ThisType <: FType] {
                          def deepCopy(): ThisType
                          }

                          class ConcreteType extends FType[ConcreteType] {
                          override def deepCopy(): ConcreteType = this
                          }

                          // in object FType
                          def deepCopy[T <: FType[T]](_type: T): T = {
                          _type.deepCopy()
                          }


                          Note the difference: in FType.deepCopy the compiler knows that the return type of _type.deepCopy() is T.



                          You can do the same with type members:



                          def deepCopy[T <: FType { type ThisType <: T }](_type: T): T = 
                          _type.deepCopy()






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Nov 26 '18 at 12:43









                          Alexey RomanovAlexey Romanov

                          111k26215357




                          111k26215357






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Stack Overflow!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53480975%2ff-bounded-types-and-methods-with-type-parameters-at-argument-and-return-sites%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Create new schema in PostgreSQL using DBeaver

                              Deepest pit of an array with Javascript: test on Codility

                              Costa Masnaga