2015-06-11 2 views
1

В Haskell я могу определить общий тип для дерева как:Scala дженерик: специализирующийся метод

type Tree t = Leaf t | Node (Tree t) (Tree t) 

Если я хочу, чтобы определить функцию для конкретной параметризации дерева, я могу просто сделать:

-- Signature of a function that takes a tree of bool 
foo :: Tree Bool -> Int 

-- Takes a tree of numbers 
bar :: (Num n) => Tree n -> Bool 

Мы можем определить подобный тип дерева в Scala с:

abstract class Tree[T]() 
case class Leaf[T](t: T) extends Tree[T] 
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T] 

Но как я могу определить метод для дерева, что о nly относится к определенным типам? Нужно ли мне использовать наследование или есть способ сказать:

abstract class Tree[T]() { 
    // Method only for Tree[String]: 
    def foo[String] = ... 
} 
+0

Что произойдет с 'foo', если тип' Tree' был 'Int'? Что вы ожидаете? 'val t: Tree [Int] = ...' и 't.foo()'? –

+0

Я бы ожидал, что foo (как пример Haskell) будет определен только для Tree [Int].Вот почему я задаюсь вопросом, нужно ли мне использовать наследование для создания нового класса из дерева, но, похоже, он нарисовал новый класс, чтобы определить специализированный метод. – Suugaku

+3

Вместо добавления метода к самому «Дереву» вы можете создать функцию, которая берет дерево в качестве параметра. Функция может объявлять определенный тип дерева (например, 'def foo (tree: Tree [String])) или он может использовать шаблон типа типа, если вы хотите сделать его общим. –

ответ

1

Это не может быть ответ, который вы ищете, так как я не сделал много Haskell, но это возможность: Вы можете определить trait которые могут быть смешаны только в конкретных случаях дерева:

trait StringFooFunctionality { 
    this: Tree[String] => // Selftype, can only be mixed in to classes that are Tree[String] 
    def foo = "Yay" // String is the datatype of Tree here 
} 

Вы бы использовать это так:

val sNode = new Node(Leaf("a"), Leaf("b")) with StringFooFunctionality 
sNode.foo 
// Yay 

недостатком является то, что оно явно должно быть смешано в о n создание объекта.

Другая возможность заключается в том, чтобы создать новую черту под названием StringTree:

trait StringTree extends Tree[String] { 
    def foo = ... 
} 

Но вы должны определить другие String типов данных:

case class StringLeaf(t: String) extends StringTree 
case class StringNode(left: StringTree, right: StringTree) extends StringTree 

И когда вы столкнулись с Tree[T] вы можете матч шаблона на нем посмотреть, есть ли это StringTree.

2

В типах Haskell нет таких методов экземпляра, как Scala.

foo в вашем примере должен быть определен (предпочтительно) в сопутствующем объекте Tree.

sealed abstract class Tree[T]() 
case class Leaf[T](t: T) extends Tree[T] 
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T] 

object Tree { 
    // Method only for Tree[String]: 
    def foo(tree: Tree[String]) = ... 
} 

PS: ИМО sealed класс или черта здесь более уместен. (Scala's sealed abstract vs abstract class)

PS II: Я просто набрал комментарий Грегора Раймана в качестве ответа.

0

Очевидным способом (и эквивалентом Haskell) является определение метода, который принимает Tree[String] как аргумент, как в ответе мухука. Если вы хотите, чтобы это выглядело как метод на Tree[String], вы можете использовать неявный класс:

implicit class Foo(val tree: Tree[String]) { 
    def foo = ... 
} 

val tree: Tree[String] = ... 

tree.foo // Foo needs to be in scope here 

Я рекомендую избегать ответа АКОС Krivachy в большинстве случаев.

Смежные вопросы